OSDN Git Service

* crontab: Spread snapshot building more evenly throughout the
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 3631615..cdc5ceb 100644 (file)
@@ -48,9 +48,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "basic-block.h"
 #include "tree-mudflap.h"
 
-#define CALLED_AS_BUILT_IN(NODE) \
-   (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
-
 #ifndef PAD_VARARGS_DOWN
 #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
 #endif
@@ -59,8 +56,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 const char *const built_in_class_names[4]
   = {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"};
 
-#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM) #X,
-const char *const built_in_names[(int) END_BUILTINS] =
+#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM, COND) #X,
+const char * built_in_names[(int) END_BUILTINS] =
 {
 #include "builtins.def"
 };
@@ -98,6 +95,7 @@ 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_int_roundingfn (tree, rtx, rtx);
 static rtx expand_builtin_args_info (tree);
 static rtx expand_builtin_next_arg (void);
 static rtx expand_builtin_va_start (tree);
@@ -113,21 +111,21 @@ static rtx expand_builtin_strspn (tree, rtx, enum machine_mode);
 static rtx expand_builtin_strcspn (tree, rtx, enum machine_mode);
 static rtx expand_builtin_memcpy (tree, rtx, enum machine_mode);
 static rtx expand_builtin_mempcpy (tree, tree, rtx, enum machine_mode, int);
-static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode);
-static rtx expand_builtin_bcopy (tree, tree);
+static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode, tree);
+static rtx expand_builtin_bcopy (tree);
 static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode);
 static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode);
 static rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
 static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode);
 static rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode);
 static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode);
-static rtx expand_builtin_memset (tree, rtx, enum machine_mode);
+static rtx expand_builtin_memset (tree, rtx, enum machine_mode, tree);
 static rtx expand_builtin_bzero (tree);
 static rtx expand_builtin_strlen (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strstr (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strpbrk (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strchr (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strrchr (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strstr (tree, tree, rtx, enum machine_mode);
+static rtx expand_builtin_strpbrk (tree, tree, rtx, enum machine_mode);
+static rtx expand_builtin_strchr (tree, tree, rtx, enum machine_mode);
+static rtx expand_builtin_strrchr (tree, tree, rtx, enum machine_mode);
 static rtx expand_builtin_alloca (tree, rtx);
 static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab);
 static rtx expand_builtin_frame_address (tree, tree);
@@ -144,7 +142,7 @@ static tree fold_builtin_inf (tree, int);
 static tree fold_builtin_nan (tree, tree, int);
 static int validate_arglist (tree, ...);
 static bool integer_valued_real_p (tree);
-static tree fold_trunc_transparent_mathfn (tree);
+static tree fold_trunc_transparent_mathfn (tree, tree);
 static bool readonly_data_expr (tree);
 static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_signbit (tree, rtx);
@@ -152,41 +150,57 @@ static tree fold_builtin_cabs (tree, tree);
 static tree fold_builtin_sqrt (tree, tree);
 static tree fold_builtin_cbrt (tree, tree);
 static tree fold_builtin_pow (tree, tree, tree);
+static tree fold_builtin_powi (tree, tree, tree);
 static tree fold_builtin_sin (tree);
 static tree fold_builtin_cos (tree, tree, tree);
 static tree fold_builtin_tan (tree);
 static tree fold_builtin_atan (tree, tree);
-static tree fold_builtin_trunc (tree);
-static tree fold_builtin_floor (tree);
-static tree fold_builtin_ceil (tree);
-static tree fold_builtin_round (tree);
-static tree fold_builtin_bitop (tree);
-static tree fold_builtin_memcpy (tree);
+static tree fold_builtin_trunc (tree, tree);
+static tree fold_builtin_floor (tree, tree);
+static tree fold_builtin_ceil (tree, tree);
+static tree fold_builtin_round (tree, tree);
+static tree fold_builtin_int_roundingfn (tree, tree);
+static tree fold_builtin_bitop (tree, tree);
+static tree fold_builtin_memcpy (tree, tree);
 static tree fold_builtin_mempcpy (tree, tree, int);
 static tree fold_builtin_memmove (tree, tree);
-static tree fold_builtin_strchr (tree);
+static tree fold_builtin_strchr (tree, tree);
 static tree fold_builtin_memcmp (tree);
 static tree fold_builtin_strcmp (tree);
 static tree fold_builtin_strncmp (tree);
-static tree fold_builtin_signbit (tree);
-static tree fold_builtin_copysign (tree, tree);
+static tree fold_builtin_signbit (tree, tree);
+static tree fold_builtin_copysign (tree, tree, tree);
 static tree fold_builtin_isascii (tree);
 static tree fold_builtin_toascii (tree);
 static tree fold_builtin_isdigit (tree);
 static tree fold_builtin_fabs (tree, tree);
 static tree fold_builtin_abs (tree, tree);
-static tree fold_builtin_unordered_cmp (tree, enum tree_code, enum tree_code);
-static tree fold_builtin_1 (tree, bool);
+static tree fold_builtin_unordered_cmp (tree, tree, enum tree_code,
+                                       enum tree_code);
+static tree fold_builtin_1 (tree, tree, bool);
 
-static tree fold_builtin_strpbrk (tree);
-static tree fold_builtin_strstr (tree);
-static tree fold_builtin_strrchr (tree);
+static tree fold_builtin_strpbrk (tree, tree);
+static tree fold_builtin_strstr (tree, tree);
+static tree fold_builtin_strrchr (tree, tree);
 static tree fold_builtin_strcat (tree);
 static tree fold_builtin_strncat (tree);
 static tree fold_builtin_strspn (tree);
 static tree fold_builtin_strcspn (tree);
 static tree fold_builtin_sprintf (tree, int);
 
+/* Return true if NODE should be considered for inline expansion regardless
+   of the optimization level.  This means whenever a function is invoked with
+   its "internal" name, which normally contains the prefix "__builtin".  */
+
+static bool called_as_built_in (tree node)
+{
+  const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
+  if (strncmp (name, "__builtin_", 10) == 0)
+    return true;
+  if (strncmp (name, "__sync_", 7) == 0)
+    return true;
+  return false;
+}
 
 /* Return the alignment in bits of EXP, a pointer valued expression.
    But don't return more than MAX_ALIGN no matter what.
@@ -337,7 +351,7 @@ c_strlen (tree src, int only_value)
      runtime.  */
   if (offset < 0 || offset > max)
     {
-      warning ("offset outside bounds of constant string");
+      warning (0, "offset outside bounds of constant string");
       return 0;
     }
 
@@ -452,11 +466,16 @@ builtin_save_expr (tree exp)
    address located within it (depending on FNDECL_CODE).  */
 
 static rtx
-expand_builtin_return_addr (enum built_in_function fndecl_code, int count,
-                           rtx tem)
+expand_builtin_return_addr (enum built_in_function fndecl_code, int count)
 {
   int i;
 
+#ifdef INITIAL_FRAME_ADDRESS_RTX
+  rtx tem = INITIAL_FRAME_ADDRESS_RTX;
+#else
+  rtx tem = hard_frame_pointer_rtx;
+#endif
+
   /* Some machines need special handling before we can access
      arbitrary frames.  For example, on the sparc, we must first flush
      all register windows to the stack.  */
@@ -926,7 +945,7 @@ expand_builtin_prefetch (tree arglist)
   /* Argument 1 must be either zero or one.  */
   if (INTVAL (op1) != 0 && INTVAL (op1) != 1)
     {
-      warning ("invalid second argument to %<__builtin_prefetch%>;"
+      warning (0, "invalid second argument to %<__builtin_prefetch%>;"
               " using zero");
       op1 = const0_rtx;
     }
@@ -941,7 +960,7 @@ expand_builtin_prefetch (tree arglist)
   /* Argument 2 must be 0, 1, 2, or 3.  */
   if (INTVAL (op2) < 0 || INTVAL (op2) > 3)
     {
-      warning ("invalid third argument to %<__builtin_prefetch%>; using zero");
+      warning (0, "invalid third argument to %<__builtin_prefetch%>; using zero");
       op2 = const0_rtx;
     }
 
@@ -972,12 +991,8 @@ expand_builtin_prefetch (tree arglist)
 static rtx
 get_memory_rtx (tree exp)
 {
-  rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_SUM);
-  rtx mem;
-
-  addr = convert_memory_address (Pmode, addr);
-
-  mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
+  rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
+  rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
 
   /* Get an expression we can use to find the attributes to assign to MEM.
      If it is an ADDR_EXPR, use the operand.  Otherwise, dereference it if
@@ -1475,7 +1490,6 @@ type_to_class (tree type)
     case QUAL_UNION_TYPE:  return union_type_class;
     case ARRAY_TYPE:      return (TYPE_STRING_FLAG (type)
                                   ? string_type_class : array_type_class);
-    case FILE_TYPE:       return file_type_class;
     case LANG_TYPE:       return lang_type_class;
     default:              return no_type_class;
     }
@@ -1545,8 +1559,12 @@ mathfn_built_in (tree type, enum built_in_function fn)
       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 (BUILT_IN_LLCEIL)
+      CASE_MATHFN (BUILT_IN_LLFLOOR)
       CASE_MATHFN (BUILT_IN_LLRINT)
       CASE_MATHFN (BUILT_IN_LLROUND)
       CASE_MATHFN (BUILT_IN_LOG)
@@ -1751,6 +1769,13 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
     case BUILT_IN_RINTF:
     case BUILT_IN_RINTL:
       builtin_optab = rint_optab; break;
+    case BUILT_IN_LRINT:
+    case BUILT_IN_LRINTF:
+    case BUILT_IN_LRINTL:
+    case BUILT_IN_LLRINT:
+    case BUILT_IN_LLRINTF:
+    case BUILT_IN_LLRINTL:
+      builtin_optab = lrint_optab; break;
     default:
       gcc_unreachable ();
     }
@@ -1861,6 +1886,7 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
 {
   optab builtin_optab;
   rtx op0, op1, insns;
+  int op1_type = REAL_TYPE;
   tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   tree arg0, arg1, temp, narg;
@@ -1868,7 +1894,12 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
   bool errno_set = true;
   bool stable = true;
 
-  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+  if ((DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXP)
+      || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPF)
+      || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPL))
+    op1_type = INTEGER_TYPE;
+
+  if (!validate_arglist (arglist, REAL_TYPE, op1_type, VOID_TYPE))
     return 0;
 
   arg0 = TREE_VALUE (arglist);
@@ -1884,6 +1915,10 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
     case BUILT_IN_ATAN2F:
     case BUILT_IN_ATAN2L:
       builtin_optab = atan2_optab; break;
+    case BUILT_IN_LDEXP:
+    case BUILT_IN_LDEXPF:
+    case BUILT_IN_LDEXPL:
+      builtin_optab = ldexp_optab; break;
     case BUILT_IN_FMOD:
     case BUILT_IN_FMODF:
     case BUILT_IN_FMODL:
@@ -1973,7 +2008,7 @@ static rtx
 expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget)
 {
   optab builtin_optab;
-  rtx op0, insns, before_call;
+  rtx op0, insns;
   tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   enum machine_mode mode;
@@ -2088,13 +2123,118 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget)
       end_sequence ();
     }
 
-  before_call = get_last_insn ();
-
   target = expand_call (exp, target, target == const0_rtx);
 
   return target;
 }
 
+/* Expand a call to one of the builtin rounding functions (lfloor).
+   If expanding via optab fails, lower expression to (int)(floor(x)).
+   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.  */
+
+static rtx
+expand_builtin_int_roundingfn (tree exp, rtx target, rtx subtarget)
+{
+  optab builtin_optab;
+  rtx op0, insns, tmp;
+  tree fndecl = get_callee_fndecl (exp);
+  tree arglist = TREE_OPERAND (exp, 1);
+  enum built_in_function fallback_fn;
+  tree fallback_fndecl;
+  enum machine_mode mode;
+  tree arg, narg;
+
+  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    gcc_unreachable ();
+
+  arg = TREE_VALUE (arglist);
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_LCEIL:
+    case BUILT_IN_LCEILF:
+    case BUILT_IN_LCEILL:
+    case BUILT_IN_LLCEIL:
+    case BUILT_IN_LLCEILF:
+    case BUILT_IN_LLCEILL:
+      builtin_optab = lceil_optab;
+      fallback_fn = BUILT_IN_CEIL;
+      break;
+
+    case BUILT_IN_LFLOOR:
+    case BUILT_IN_LFLOORF:
+    case BUILT_IN_LFLOORL:
+    case BUILT_IN_LLFLOOR:
+    case BUILT_IN_LLFLOORF:
+    case BUILT_IN_LLFLOORL:
+      builtin_optab = lfloor_optab;
+      fallback_fn = BUILT_IN_FLOOR;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Make a suitable register to place result in.  */
+  mode = TYPE_MODE (TREE_TYPE (exp));
+
+  /* Before working hard, check whether the instruction is available.  */
+  if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      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.  */
+      narg = builtin_save_expr (arg);
+      if (narg != arg)
+       {
+         arg = narg;
+         arglist = build_tree_list (NULL_TREE, arg);
+         exp = build_function_call_expr (fndecl, arglist);
+       }
+
+      op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+
+      start_sequence ();
+
+      /* Compute into TARGET.
+        Set TARGET to wherever the result comes back.  */
+      target = expand_unop (mode, builtin_optab, op0, target, 0);
+
+      if (target != 0)
+       {
+         /* 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).  */
+      end_sequence ();
+    }
+
+  /* Fall back to floating point rounding optab.  */
+  fallback_fndecl = mathfn_built_in (TREE_TYPE (arg), fallback_fn);
+  /* We shouldn't get here on targets without TARGET_C99_FUNCTIONS.
+     ??? Perhaps convert (int)floorf(x) into (int)floor((double)x).  */
+  gcc_assert (fallback_fndecl != NULL_TREE);
+  exp = build_function_call_expr (fallback_fndecl, arglist);
+
+  tmp = expand_builtin_mathfn (exp, NULL_RTX, NULL_RTX);
+
+  /* Truncate the result of floating point optab to integer
+     via expand_fix ().  */
+  target = gen_reg_rtx (mode);
+  expand_fix (target, tmp, 0);
+
+  return target;
+}
+
 /* To evaluate powi(x,n), the floating point value x raised to the
    constant integer exponent n, we use a hybrid algorithm that
    combines the "window method" with look-up tables.  For an
@@ -2362,6 +2502,7 @@ expand_builtin_powi (tree exp, rtx target, rtx subtarget)
   tree arg0, arg1;
   rtx op0, op1;
   enum machine_mode mode;
+  enum machine_mode mode2;
 
   if (! validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE))
     return 0;
@@ -2393,19 +2534,22 @@ expand_builtin_powi (tree exp, rtx target, rtx subtarget)
 
   /* Emit a libcall to libgcc.  */
 
+  /* Mode of the 2nd argument must match that of an int. */
+  mode2 = mode_for_size (INT_TYPE_SIZE, MODE_INT, 0);
+
   if (target == NULL_RTX)
     target = gen_reg_rtx (mode);
 
   op0 = expand_expr (arg0, subtarget, mode, 0);
   if (GET_MODE (op0) != mode)
     op0 = convert_to_mode (mode, op0, 0);
-  op1 = expand_expr (arg1, 0, word_mode, 0);
-  if (GET_MODE (op1) != word_mode)
-    op1 = convert_to_mode (word_mode, op1, 0);
+  op1 = expand_expr (arg1, 0, mode2, 0);
+  if (GET_MODE (op1) != mode2)
+    op1 = convert_to_mode (mode2, op1, 0);
 
   target = emit_library_call_value (powi_optab->handlers[(int) mode].libfunc,
                                    target, LCT_CONST_MAKE_BLOCK, mode, 2,
-                                   op0, mode, op1, word_mode);
+                                   op0, mode, op1, mode2);
 
   return target;
 }
@@ -2523,11 +2667,11 @@ expand_builtin_strlen (tree arglist, rtx target,
    in TARGET, if convenient (and in mode MODE if that's convenient).  */
 
 static rtx
-expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strstr (tree arglist, tree type, rtx target, enum machine_mode mode)
 {
   if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     {
-      tree result = fold_builtin_strstr (arglist);
+      tree result = fold_builtin_strstr (arglist, type);
       if (result)
        return expand_expr (result, target, mode, EXPAND_NORMAL);
     }
@@ -2539,11 +2683,11 @@ expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode)
    in TARGET, if convenient (and in mode MODE if that's convenient).  */
 
 static rtx
-expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strchr (tree arglist, tree type, rtx target, enum machine_mode mode)
 {
   if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     {
-      tree result = fold_builtin_strchr (arglist);
+      tree result = fold_builtin_strchr (arglist, type);
       if (result)
        return expand_expr (result, target, mode, EXPAND_NORMAL);
 
@@ -2557,11 +2701,11 @@ expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode)
    in TARGET, if convenient (and in mode MODE if that's convenient).  */
 
 static rtx
-expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strrchr (tree arglist, tree type, rtx target, enum machine_mode mode)
 {
   if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     {
-      tree result = fold_builtin_strrchr (arglist);
+      tree result = fold_builtin_strrchr (arglist, type);
       if (result)
        return expand_expr (result, target, mode, EXPAND_NORMAL);
     }
@@ -2573,11 +2717,11 @@ expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode)
    in TARGET, if convenient (and in mode MODE if that's convenient).  */
 
 static rtx
-expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strpbrk (tree arglist, tree type, rtx target, enum machine_mode mode)
 {
   if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     {
-      tree result = fold_builtin_strpbrk (arglist);
+      tree result = fold_builtin_strpbrk (arglist, type);
       if (result)
        return expand_expr (result, target, mode, EXPAND_NORMAL);
     }
@@ -2608,6 +2752,7 @@ builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset,
 static rtx
 expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
 {
+  tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   if (!validate_arglist (arglist,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
@@ -2622,7 +2767,7 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
       unsigned int dest_align
        = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
       rtx dest_mem, src_mem, dest_addr, len_rtx;
-      tree result = fold_builtin_memcpy (exp);
+      tree result = fold_builtin_memcpy (fndecl, arglist);
 
       if (result)
        return expand_expr (result, target, mode, EXPAND_NORMAL);
@@ -2663,7 +2808,8 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
 
       /* Copy word part most expediently.  */
       dest_addr = emit_block_move (dest_mem, src_mem, len_rtx,
-                                  BLOCK_OP_NORMAL);
+                                  CALL_EXPR_TAILCALL (exp)
+                                  ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
 
       if (dest_addr == 0)
        {
@@ -2770,7 +2916,7 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m
 
 static rtx
 expand_builtin_memmove (tree arglist, tree type, rtx target,
-                       enum machine_mode mode)
+                       enum machine_mode mode, tree orig_exp)
 {
   if (!validate_arglist (arglist,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
@@ -2802,11 +2948,13 @@ expand_builtin_memmove (tree arglist, tree type, rtx target,
         normal memcpy.  */
       if (readonly_data_expr (src))
         {
-         tree const fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+         tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
          if (!fn)
            return 0;
-         return expand_expr (build_function_call_expr (fn, arglist),
-                             target, mode, EXPAND_NORMAL);
+         fn = build_function_call_expr (fn, arglist);
+         if (TREE_CODE (fn) == CALL_EXPR)
+           CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp);
+         return expand_expr (fn, target, mode, EXPAND_NORMAL);
        }
 
       /* If length is 1 and we can expand memcpy call inline,
@@ -2828,8 +2976,10 @@ expand_builtin_memmove (tree arglist, tree type, rtx target,
    if we failed the caller should emit a normal call.  */
 
 static rtx
-expand_builtin_bcopy (tree arglist, tree type)
+expand_builtin_bcopy (tree exp)
 {
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree type = TREE_TYPE (exp);
   tree src, dest, size, newarglist;
 
   if (!validate_arglist (arglist,
@@ -2849,7 +2999,7 @@ expand_builtin_bcopy (tree arglist, tree type)
   newarglist = tree_cons (NULL_TREE, src, newarglist);
   newarglist = tree_cons (NULL_TREE, dest, newarglist);
 
-  return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode);
+  return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp);
 }
 
 #ifndef HAVE_movstr
@@ -2927,10 +3077,11 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
 static rtx
 expand_builtin_strcpy (tree exp, rtx target, enum machine_mode mode)
 {
+  tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     {
-      tree result = fold_builtin_strcpy (exp, 0);
+      tree result = fold_builtin_strcpy (fndecl, arglist, 0);
       if (result)
        return expand_expr (result, target, mode, EXPAND_NORMAL);
 
@@ -3047,13 +3198,14 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
 static rtx
 expand_builtin_strncpy (tree exp, rtx target, enum machine_mode mode)
 {
+  tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   if (validate_arglist (arglist,
                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     {
       tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
       tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
-      tree result = fold_builtin_strncpy (exp, slen);
+      tree result = fold_builtin_strncpy (fndecl, arglist, slen);
       
       if (result)
        return expand_expr (result, target, mode, EXPAND_NORMAL);
@@ -3141,7 +3293,8 @@ builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
    convenient).  */
 
 static rtx
-expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode,
+                      tree orig_exp)
 {
   if (!validate_arglist (arglist,
                         POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
@@ -3227,7 +3380,9 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
 
       dest_mem = get_memory_rtx (dest);
       set_mem_align (dest_mem, dest_align);
-      dest_addr = clear_storage (dest_mem, len_rtx);
+      dest_addr = clear_storage (dest_mem, len_rtx,
+                                CALL_EXPR_TAILCALL (orig_exp)
+                                ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
 
       if (dest_addr == 0)
        {
@@ -3243,8 +3398,9 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
    if we failed the caller should emit a normal call.  */
 
 static rtx
-expand_builtin_bzero (tree arglist)
+expand_builtin_bzero (tree exp)
 {
+  tree arglist = TREE_OPERAND (exp, 1);
   tree dest, size, newarglist;
 
   if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
@@ -3262,7 +3418,7 @@ expand_builtin_bzero (tree arglist)
   newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist);
   newarglist = tree_cons (NULL_TREE, dest, newarglist);
 
-  return expand_builtin_memset (newarglist, const0_rtx, VOIDmode);
+  return expand_builtin_memset (newarglist, const0_rtx, VOIDmode, exp);
 }
 
 /* Expand expression EXP, which is a call to the memcmp built-in function.
@@ -3401,7 +3557,7 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
     tree len, len1, len2;
     rtx arg1_rtx, arg2_rtx, arg3_rtx;
     rtx result, insn;
-    tree fndecl;
+    tree fndecl, fn;
 
     int arg1_align
       = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@@ -3485,8 +3641,10 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
     arglist = build_tree_list (NULL_TREE, arg2);
     arglist = tree_cons (NULL_TREE, arg1, arglist);
     fndecl = get_callee_fndecl (exp);
-    exp = build_function_call_expr (fndecl, arglist);
-    return expand_call (exp, target, target == const0_rtx);
+    fn = build_function_call_expr (fndecl, arglist);
+    if (TREE_CODE (fn) == CALL_EXPR)
+      CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+    return expand_call (fn, target, target == const0_rtx);
   }
 #endif
   return 0;
@@ -3523,7 +3681,7 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
     tree len, len1, len2;
     rtx arg1_rtx, arg2_rtx, arg3_rtx;
     rtx result, insn;
-    tree fndecl;
+    tree fndecl, fn;
 
     int arg1_align
       = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@@ -3613,8 +3771,10 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
     arglist = tree_cons (NULL_TREE, arg2, arglist);
     arglist = tree_cons (NULL_TREE, arg1, arglist);
     fndecl = get_callee_fndecl (exp);
-    exp = build_function_call_expr (fndecl, arglist);
-    return expand_call (exp, target, target == const0_rtx);
+    fn = build_function_call_expr (fndecl, arglist);
+    if (TREE_CODE (fn) == CALL_EXPR)
+      CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+    return expand_call (fn, target, target == const0_rtx);
   }
 #endif
   return 0;
@@ -3663,7 +3823,8 @@ expand_builtin_strcat (tree arglist, tree type, rtx target, enum machine_mode mo
                fold (build_function_call_expr (strlen_fn,
                                                build_tree_list (NULL_TREE,
                                                                 dst)));
-             /* Create (dst + strlen (dst)).  */
+             /* Create (dst + (cast) strlen (dst)).  */
+             newdst = fold_convert (TREE_TYPE (dst), newdst);
              newdst = fold (build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst));
 
              /* Prepend the new dst argument.  */
@@ -4072,12 +4233,12 @@ gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p)
       /* Unfortunately, this is merely undefined, rather than a constraint
         violation, so we cannot make this an error.  If this call is never
         executed, the program is still strictly conforming.  */
-      warning ("%qT is promoted to %qT when passed through %<...%>",
+      warning (0, "%qT is promoted to %qT when passed through %<...%>",
               type, promoted_type);
       if (! gave_help)
        {
          gave_help = true;
-         warning ("(so you should pass %qT not %qT to %<va_arg%>)",
+         warning (0, "(so you should pass %qT not %qT to %<va_arg%>)",
                   promoted_type, type);
        }
 
@@ -4114,7 +4275,8 @@ gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p)
        gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
 
       if (!targetm.gimplify_va_arg_expr)
-       /* Once most targets are converted this should abort.  */
+       /* FIXME:Once most targets are converted we should merely
+          assert this is non-null.  */
        return GS_ALL_DONE;
 
       *expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p);
@@ -4210,16 +4372,15 @@ expand_builtin_frame_address (tree fndecl, tree arglist)
     {
       rtx tem
        = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl),
-                                     tree_low_cst (TREE_VALUE (arglist), 1),
-                                     hard_frame_pointer_rtx);
+                                     tree_low_cst (TREE_VALUE (arglist), 1));
 
       /* Some ports cannot access arbitrary stack frames.  */
       if (tem == NULL)
        {
          if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
-           warning ("unsupported argument to %<__builtin_frame_address%>");
+           warning (0, "unsupported argument to %<__builtin_frame_address%>");
          else
-           warning ("unsupported argument to %<__builtin_return_address%>");
+           warning (0, "unsupported argument to %<__builtin_return_address%>");
          return const0_rtx;
        }
 
@@ -4545,15 +4706,16 @@ build_string_literal (int len, const char *str)
   return t;
 }
 
-/* Expand a call to printf or printf_unlocked with argument list ARGLIST.
+/* Expand EXP, a call to printf or printf_unlocked.
    Return 0 if a normal call should be emitted rather than transforming
    the function inline.  If convenient, the result should be placed in
    TARGET with mode MODE.  UNLOCKED indicates this is a printf_unlocked
    call.  */
 static rtx
-expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode,
+expand_builtin_printf (tree exp, rtx target, enum machine_mode mode,
                       bool unlocked)
 {
+  tree arglist = TREE_OPERAND (exp, 1);
   tree fn_putchar = unlocked
                    ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
                    : implicit_built_in_decls[BUILT_IN_PUTCHAR];
@@ -4644,19 +4806,22 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode,
 
   if (!fn)
     return 0;
-  return expand_expr (build_function_call_expr (fn, arglist),
-                     target, mode, EXPAND_NORMAL);
+  fn = build_function_call_expr (fn, arglist);
+  if (TREE_CODE (fn) == CALL_EXPR)
+    CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+  return expand_expr (fn, target, mode, EXPAND_NORMAL);
 }
 
-/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST.
+/* Expand EXP, a call to fprintf or fprintf_unlocked.
    Return 0 if a normal call should be emitted rather than transforming
    the function inline.  If convenient, the result should be placed in
    TARGET with mode MODE.  UNLOCKED indicates this is a fprintf_unlocked
    call.  */
 static rtx
-expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode,
+expand_builtin_fprintf (tree exp, rtx target, enum machine_mode mode,
                        bool unlocked)
 {
+  tree arglist = TREE_OPERAND (exp, 1);
   tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
                           : implicit_built_in_decls[BUILT_IN_FPUTC];
   tree fn_fputs = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED]
@@ -4738,8 +4903,10 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode,
 
   if (!fn)
     return 0;
-  return expand_expr (build_function_call_expr (fn, arglist),
-                     target, mode, EXPAND_NORMAL);
+  fn = build_function_call_expr (fn, arglist);
+  if (TREE_CODE (fn) == CALL_EXPR)
+    CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+  return expand_expr (fn, target, mode, EXPAND_NORMAL);
 }
 
 /* Expand a call to sprintf with argument list ARGLIST.  Return 0 if
@@ -4844,7 +5011,7 @@ expand_builtin_profile_func (bool exitp)
 
   emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode,
                     expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
-                                                0, hard_frame_pointer_rtx),
+                                                0),
                     Pmode);
 
   return const0_rtx;
@@ -4941,7 +5108,7 @@ expand_builtin_signbit (tree exp, rtx target)
   enum machine_mode fmode, imode, rmode;
   HOST_WIDE_INT hi, lo;
   tree arg, arglist;
-  int bitpos;
+  int word, bitpos;
   rtx temp;
 
   arglist = TREE_OPERAND (exp, 1);
@@ -4955,7 +5122,8 @@ expand_builtin_signbit (tree exp, rtx target)
 
   /* For floating point formats without a sign bit, implement signbit
      as "ARG < 0.0".  */
-  if (fmt->signbit < 0)
+  bitpos = fmt->signbit_ro;
+  if (bitpos < 0)
   {
     /* But we can't do this if the format supports signed zero.  */
     if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode))
@@ -4966,41 +5134,37 @@ expand_builtin_signbit (tree exp, rtx target)
     return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
   }
 
-  imode = int_mode_for_mode (fmode);
-  if (imode == BLKmode)
-    return 0;
-
-  bitpos = fmt->signbit;
-  /* Handle targets with different FP word orders.  */
-  if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+  temp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
+  if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD)
     {
-      int nwords = GET_MODE_BITSIZE (fmode) / BITS_PER_WORD;
-      int word = nwords - (bitpos / BITS_PER_WORD) - 1;
-      bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
+      imode = int_mode_for_mode (fmode);
+      if (imode == BLKmode)
+       return 0;
+      temp = gen_lowpart (imode, temp);
+    }
+  else
+    {
+      imode = word_mode;
+      /* Handle targets with different FP word orders.  */
+      if (FLOAT_WORDS_BIG_ENDIAN)
+        word = (GET_MODE_BITSIZE (fmode) - bitpos) / BITS_PER_WORD;
+      else
+        word = bitpos / BITS_PER_WORD;
+      temp = operand_subword_force (temp, word, fmode);
+      bitpos = bitpos % BITS_PER_WORD;
     }
 
-  /* If the sign bit is not in the lowpart and the floating point format
-     is wider than an integer, check that is twice the size of an integer
-     so that we can use gen_highpart below.  */
-  if (bitpos >= GET_MODE_BITSIZE (rmode)
-      && GET_MODE_BITSIZE (imode) != 2 * GET_MODE_BITSIZE (rmode))
-    return 0;
+  /* Force the intermediate word_mode (or narrower) result into a
+     register.  This avoids attempting to create paradoxical SUBREGs
+     of floating point modes below.  */
+  temp = force_reg (imode, temp);
 
-  temp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
-  temp = gen_lowpart (imode, temp);
+  /* If the bitpos is within the "result mode" lowpart, the operation
+     can be implement with a single bitwise AND.  Otherwise, we need
+     a right shift and an AND.  */
 
-  if (GET_MODE_BITSIZE (imode) > GET_MODE_BITSIZE (rmode))
+  if (bitpos < GET_MODE_BITSIZE (rmode))
     {
-      if (BYTES_BIG_ENDIAN)
-       bitpos = GET_MODE_BITSIZE (imode) - 1 - bitpos;
-      temp = copy_to_mode_reg (imode, temp);
-      temp = extract_bit_field (temp, 1, bitpos, 1,
-                               NULL_RTX, rmode, rmode);
-    }
-  else
-    {
-      if (GET_MODE_BITSIZE (imode) < GET_MODE_BITSIZE (rmode))
-       temp = gen_lowpart (rmode, temp);
       if (bitpos < HOST_BITS_PER_WIDE_INT)
        {
          hi = 0;
@@ -5012,11 +5176,24 @@ expand_builtin_signbit (tree exp, rtx target)
          lo = 0;
        }
 
-      temp = force_reg (rmode, temp);
+      if (imode != rmode)
+       temp = gen_lowpart (rmode, temp);
       temp = expand_binop (rmode, and_optab, temp,
                           immed_double_const (lo, hi, rmode),
-                          target, 1, OPTAB_LIB_WIDEN);
+                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
+    }
+  else
+    {
+      /* Perform a logical right shift to place the signbit in the least
+         significant bit, then truncate the result to the desired mode
+        and mask just this bit.  */
+      temp = expand_shift (RSHIFT_EXPR, imode, temp,
+                          build_int_cst (NULL_TREE, bitpos), NULL_RTX, 1);
+      temp = gen_lowpart (rmode, temp);
+      temp = expand_binop (rmode, and_optab, temp, const1_rtx,
+                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
     }
+
   return temp;
 }
 
@@ -5082,6 +5259,166 @@ expand_builtin_fork_or_exec (tree fn, tree arglist, rtx target, int ignore)
 
   return expand_call (call, target, ignore);
 }
+
+\f
+/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics.
+   ARGLIST is the operands list to the function.  CODE is the rtx code 
+   that corresponds to the arithmetic or logical operation from the name;
+   an exception here is that NOT actually means NAND.  TARGET is an optional
+   place for us to store the results; AFTER is true if this is the
+   fetch_and_xxx form.  IGNORE is true if we don't actually care about
+   the result of the operation at all.  */
+
+static rtx
+expand_builtin_sync_operation (tree arglist, enum rtx_code code, bool after,
+                              rtx target, bool ignore)
+{
+  enum machine_mode mode;
+  rtx addr, val, mem;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+  arglist = TREE_CHAIN (arglist);
+  val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the full barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  if (ignore)
+    return expand_sync_operation (mem, val, code);
+  else
+    return expand_sync_fetch_operation (mem, val, code, after, target);
+}
+
+/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap
+   intrinsics.  ARGLIST is the operands list to the function.  IS_BOOL is
+   true if this is the boolean form.  TARGET is a place for us to store the
+   results; this is NOT optional if IS_BOOL is true.  */
+
+static rtx
+expand_builtin_compare_and_swap (tree arglist, bool is_bool, rtx target)
+{
+  enum machine_mode mode;
+  rtx addr, old_val, new_val, mem;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+  arglist = TREE_CHAIN (arglist);
+  old_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  arglist = TREE_CHAIN (arglist);
+  new_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the full barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  if (is_bool)
+    return expand_bool_compare_and_swap (mem, old_val, new_val, target);
+  else
+    return expand_val_compare_and_swap (mem, old_val, new_val, target);
+}
+
+/* Expand the __sync_lock_test_and_set intrinsic.  Note that the most
+   general form is actually an atomic exchange, and some targets only
+   support a reduced form with the second argument being a constant 1.
+   ARGLIST is the operands list to the function; TARGET is an optional
+   place for us to store the results.  */
+
+static rtx
+expand_builtin_lock_test_and_set (tree arglist, rtx target)
+{
+  enum machine_mode mode;
+  rtx addr, val, mem;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+  arglist = TREE_CHAIN (arglist);
+  val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  return expand_sync_lock_test_and_set (mem, val, target);
+}
+
+/* Expand the __sync_synchronize intrinsic.  */
+
+static void
+expand_builtin_synchronize (void)
+{
+  rtx body;
+
+#ifdef HAVE_memory_barrier
+  if (HAVE_memory_barrier)
+    {
+      emit_insn (gen_memory_barrier ());
+      return;
+    }
+#endif
+
+  /* If no explicit memory barrier instruction is available, create an empty
+     asm stmt that will prevent compiler movement across the barrier.  */
+  body = gen_rtx_ASM_INPUT (VOIDmode, "");
+  MEM_VOLATILE_P (body) = 1;
+  emit_insn (body);
+}
+
+/* Expand the __sync_lock_release intrinsic.  ARGLIST is the operands list
+   to the function.  */
+
+static void
+expand_builtin_lock_release (tree arglist)
+{
+  enum machine_mode mode;
+  enum insn_code icode;
+  rtx addr, val, mem, insn;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+  val = const0_rtx;
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  /* If there is an explicit operation in the md file, use it.  */
+  icode = sync_lock_release[mode];
+  if (icode != CODE_FOR_nothing)
+    {
+      if (!insn_data[icode].operand[1].predicate (val, mode))
+       val = force_reg (mode, val);
+
+      insn = GEN_FCN (icode) (mem, val);
+      if (insn)
+       {
+         emit_insn (insn);
+         return;
+       }
+    }
+
+  /* Otherwise we can implement this operation by emitting a barrier
+     followed by a store of zero.  */
+  expand_builtin_synchronize ();
+  emit_move_insn (mem, val);
+}
 \f
 /* Expand an expression EXP that calls a built-in function,
    with result going to TARGET if that's convenient
@@ -5104,7 +5441,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
   /* When not optimizing, generate calls to library functions for a certain
      set of builtins.  */
   if (!optimize
-      && !CALLED_AS_BUILT_IN (fndecl)
+      && !called_as_built_in (fndecl)
       && DECL_ASSEMBLER_NAME_SET_P (fndecl)
       && fcode != BUILT_IN_ALLOCA)
     return expand_call (exp, target, ignore);
@@ -5234,11 +5571,34 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
     case BUILT_IN_RINT:
     case BUILT_IN_RINTF:
     case BUILT_IN_RINTL:
+    case BUILT_IN_LRINT:
+    case BUILT_IN_LRINTF:
+    case BUILT_IN_LRINTL:
+    case BUILT_IN_LLRINT:
+    case BUILT_IN_LLRINTF:
+    case BUILT_IN_LLRINTL:
       target = expand_builtin_mathfn (exp, target, subtarget);
       if (target)
        return target;
       break;
 
+    case BUILT_IN_LCEIL:
+    case BUILT_IN_LCEILF:
+    case BUILT_IN_LCEILL:
+    case BUILT_IN_LLCEIL:
+    case BUILT_IN_LLCEILF:
+    case BUILT_IN_LLCEILL:
+    case BUILT_IN_LFLOOR:
+    case BUILT_IN_LFLOORF:
+    case BUILT_IN_LFLOORL:
+    case BUILT_IN_LLFLOOR:
+    case BUILT_IN_LLFLOORF:
+    case BUILT_IN_LLFLOORL:
+      target = expand_builtin_int_roundingfn (exp, target, subtarget);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_POW:
     case BUILT_IN_POWF:
     case BUILT_IN_POWL:
@@ -5258,6 +5618,9 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
     case BUILT_IN_ATAN2:
     case BUILT_IN_ATAN2F:
     case BUILT_IN_ATAN2L:
+    case BUILT_IN_LDEXP:
+    case BUILT_IN_LDEXPF:
+    case BUILT_IN_LDEXPL:
     case BUILT_IN_FMOD:
     case BUILT_IN_FMODF:
     case BUILT_IN_FMODL:
@@ -5468,27 +5831,27 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       break;
 
     case BUILT_IN_STRSTR:
-      target = expand_builtin_strstr (arglist, target, mode);
+      target = expand_builtin_strstr (arglist, TREE_TYPE (exp), target, mode);
       if (target)
        return target;
       break;
 
     case BUILT_IN_STRPBRK:
-      target = expand_builtin_strpbrk (arglist, target, mode);
+      target = expand_builtin_strpbrk (arglist, TREE_TYPE (exp), target, mode);
       if (target)
        return target;
       break;
 
     case BUILT_IN_INDEX:
     case BUILT_IN_STRCHR:
-      target = expand_builtin_strchr (arglist, target, mode);
+      target = expand_builtin_strchr (arglist, TREE_TYPE (exp), target, mode);
       if (target)
        return target;
       break;
 
     case BUILT_IN_RINDEX:
     case BUILT_IN_STRRCHR:
-      target = expand_builtin_strrchr (arglist, target, mode);
+      target = expand_builtin_strrchr (arglist, TREE_TYPE (exp), target, mode);
       if (target)
        return target;
       break;
@@ -5506,25 +5869,26 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       break;
 
     case BUILT_IN_MEMMOVE:
-      target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target, mode);
+      target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target,
+                                      mode, exp);
       if (target)
        return target;
       break;
 
     case BUILT_IN_BCOPY:
-      target = expand_builtin_bcopy (arglist, TREE_TYPE (exp));
+      target = expand_builtin_bcopy (exp);
       if (target)
        return target;
       break;
 
     case BUILT_IN_MEMSET:
-      target = expand_builtin_memset (arglist, target, mode);
+      target = expand_builtin_memset (arglist, target, mode, exp);
       if (target)
        return target;
       break;
 
     case BUILT_IN_BZERO:
-      target = expand_builtin_bzero (arglist);
+      target = expand_builtin_bzero (exp);
       if (target)
        return target;
       break;
@@ -5601,13 +5965,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       return const0_rtx;
 
     case BUILT_IN_PRINTF:
-      target = expand_builtin_printf (arglist, target, mode, false);
+      target = expand_builtin_printf (exp, target, mode, false);
       if (target)
        return target;
       break;
 
     case BUILT_IN_PRINTF_UNLOCKED:
-      target = expand_builtin_printf (arglist, target, mode, true);
+      target = expand_builtin_printf (exp, target, mode, true);
       if (target)
        return target;
       break;
@@ -5624,13 +5988,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       break;
 
     case BUILT_IN_FPRINTF:
-      target = expand_builtin_fprintf (arglist, target, mode, false);
+      target = expand_builtin_fprintf (exp, target, mode, false);
       if (target)
        return target;
       break;
 
     case BUILT_IN_FPRINTF_UNLOCKED:
-      target = expand_builtin_fprintf (arglist, target, mode, true);
+      target = expand_builtin_fprintf (exp, target, mode, true);
       if (target)
        return target;
       break;
@@ -5712,6 +6076,168 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
        return target;
       break;
 
+    case BUILT_IN_FETCH_AND_ADD_1:
+    case BUILT_IN_FETCH_AND_ADD_2:
+    case BUILT_IN_FETCH_AND_ADD_4:
+    case BUILT_IN_FETCH_AND_ADD_8:
+      target = expand_builtin_sync_operation (arglist, PLUS,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_SUB_1:
+    case BUILT_IN_FETCH_AND_SUB_2:
+    case BUILT_IN_FETCH_AND_SUB_4:
+    case BUILT_IN_FETCH_AND_SUB_8:
+      target = expand_builtin_sync_operation (arglist, MINUS,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_OR_1:
+    case BUILT_IN_FETCH_AND_OR_2:
+    case BUILT_IN_FETCH_AND_OR_4:
+    case BUILT_IN_FETCH_AND_OR_8:
+      target = expand_builtin_sync_operation (arglist, IOR,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_AND_1:
+    case BUILT_IN_FETCH_AND_AND_2:
+    case BUILT_IN_FETCH_AND_AND_4:
+    case BUILT_IN_FETCH_AND_AND_8:
+      target = expand_builtin_sync_operation (arglist, AND,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_XOR_1:
+    case BUILT_IN_FETCH_AND_XOR_2:
+    case BUILT_IN_FETCH_AND_XOR_4:
+    case BUILT_IN_FETCH_AND_XOR_8:
+      target = expand_builtin_sync_operation (arglist, XOR,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_NAND_1:
+    case BUILT_IN_FETCH_AND_NAND_2:
+    case BUILT_IN_FETCH_AND_NAND_4:
+    case BUILT_IN_FETCH_AND_NAND_8:
+      target = expand_builtin_sync_operation (arglist, NOT,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_ADD_AND_FETCH_1:
+    case BUILT_IN_ADD_AND_FETCH_2:
+    case BUILT_IN_ADD_AND_FETCH_4:
+    case BUILT_IN_ADD_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, PLUS,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_SUB_AND_FETCH_1:
+    case BUILT_IN_SUB_AND_FETCH_2:
+    case BUILT_IN_SUB_AND_FETCH_4:
+    case BUILT_IN_SUB_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, MINUS,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_OR_AND_FETCH_1:
+    case BUILT_IN_OR_AND_FETCH_2:
+    case BUILT_IN_OR_AND_FETCH_4:
+    case BUILT_IN_OR_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, IOR,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_AND_AND_FETCH_1:
+    case BUILT_IN_AND_AND_FETCH_2:
+    case BUILT_IN_AND_AND_FETCH_4:
+    case BUILT_IN_AND_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, AND,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_XOR_AND_FETCH_1:
+    case BUILT_IN_XOR_AND_FETCH_2:
+    case BUILT_IN_XOR_AND_FETCH_4:
+    case BUILT_IN_XOR_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, XOR,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_NAND_AND_FETCH_1:
+    case BUILT_IN_NAND_AND_FETCH_2:
+    case BUILT_IN_NAND_AND_FETCH_4:
+    case BUILT_IN_NAND_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, NOT,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_1:
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_2:
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_4:
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_8:
+      if (mode == VOIDmode)
+       mode = TYPE_MODE (boolean_type_node);
+      if (!target || !register_operand (target, mode))
+       target = gen_reg_rtx (mode);
+      target = expand_builtin_compare_and_swap (arglist, true, target);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_1:
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_2:
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_4:
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_8:
+      target = expand_builtin_compare_and_swap (arglist, false, target);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_LOCK_TEST_AND_SET_1:
+    case BUILT_IN_LOCK_TEST_AND_SET_2:
+    case BUILT_IN_LOCK_TEST_AND_SET_4:
+    case BUILT_IN_LOCK_TEST_AND_SET_8:
+      target = expand_builtin_lock_test_and_set (arglist, target);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_LOCK_RELEASE_1:
+    case BUILT_IN_LOCK_RELEASE_2:
+    case BUILT_IN_LOCK_RELEASE_4:
+    case BUILT_IN_LOCK_RELEASE_8:
+      expand_builtin_lock_release (arglist);
+      return const0_rtx;
+
+    case BUILT_IN_SYNCHRONIZE:
+      expand_builtin_synchronize ();
+      return const0_rtx;
+
     default:   /* just do library call, if unknown builtin */
       break;
     }
@@ -5812,10 +6338,17 @@ fold_builtin_constant_p (tree arglist)
   /* If we know this is a constant, emit the constant of one.  */
   if (CONSTANT_CLASS_P (arglist)
       || (TREE_CODE (arglist) == CONSTRUCTOR
-         && TREE_CONSTANT (arglist))
-      || (TREE_CODE (arglist) == ADDR_EXPR
-         && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST))
+         && TREE_CONSTANT (arglist)))
     return integer_one_node;
+  if (TREE_CODE (arglist) == ADDR_EXPR)
+    {
+       tree op = TREE_OPERAND (arglist, 0);
+       if (TREE_CODE (op) == STRING_CST
+          || (TREE_CODE (op) == ARRAY_REF
+              && integer_zerop (TREE_OPERAND (op, 1))
+              && TREE_CODE (TREE_OPERAND (op, 0)) == STRING_CST))
+        return integer_one_node;
+    }
 
   /* If this expression has side effects, show we don't know it to be a
      constant.  Likewise if it's a pointer or aggregate type since in
@@ -6039,10 +6572,8 @@ integer_valued_real_p (tree t)
    Do the transformation.  */
 
 static tree
-fold_trunc_transparent_mathfn (tree exp)
+fold_trunc_transparent_mathfn (tree fndecl, tree arglist)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  tree arglist = TREE_OPERAND (exp, 1);
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
   tree arg;
 
@@ -6062,7 +6593,7 @@ fold_trunc_transparent_mathfn (tree exp)
   if (optimize)
     {
       tree arg0 = strip_float_extensions (arg);
-      tree ftype = TREE_TYPE (exp);
+      tree ftype = TREE_TYPE (TREE_TYPE (fndecl));
       tree newtype = TREE_TYPE (arg0);
       tree decl;
 
@@ -6082,10 +6613,8 @@ fold_trunc_transparent_mathfn (tree exp)
    the argument, for instance lround((double)f) -> lroundf (f).  */
 
 static tree
-fold_fixed_mathfn (tree exp)
+fold_fixed_mathfn (tree fndecl, tree arglist)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  tree arglist = TREE_OPERAND (exp, 1);
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
   tree arg;
 
@@ -6097,7 +6626,7 @@ fold_fixed_mathfn (tree exp)
   /* 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 (FIX_TRUNC_EXPR, TREE_TYPE (exp), arg));
+    return fold (build1 (FIX_TRUNC_EXPR, TREE_TYPE (TREE_TYPE (fndecl)), arg));
 
   if (optimize)
     {
@@ -6292,40 +6821,82 @@ fold_builtin_cbrt (tree arglist, tree type)
   if (real_zerop (arg) || real_onep (arg) || real_minus_onep (arg))
     return arg;
 
-  /* Optimize cbrt(expN(x)) -> expN(x/3).  */
-  if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
-    {
-      tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-      const REAL_VALUE_TYPE third_trunc =
-       real_value_truncate (TYPE_MODE (type), dconstthird);
-      arg = fold (build2 (MULT_EXPR, type,
-                         TREE_VALUE (TREE_OPERAND (arg, 1)),
-                         build_real (type, third_trunc)));
-      arglist = build_tree_list (NULL_TREE, arg);
-      return build_function_call_expr (expfn, arglist);
-    }
-
-  /* Optimize cbrt(sqrt(x)) -> pow(x,1/6).  */
-  /* We don't optimize cbrt(cbrt(x)) -> pow(x,1/9) because if
-     x is negative pow will error but cbrt won't.  */
-  if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode))
+  if (flag_unsafe_math_optimizations)
     {
-      tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+      /* Optimize cbrt(expN(x)) -> expN(x/3).  */
+      if (BUILTIN_EXPONENT_P (fcode))
+        {
+         tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+         const REAL_VALUE_TYPE third_trunc =
+           real_value_truncate (TYPE_MODE (type), dconstthird);
+         arg = fold (build2 (MULT_EXPR, type,
+                             TREE_VALUE (TREE_OPERAND (arg, 1)),
+                             build_real (type, third_trunc)));
+         arglist = build_tree_list (NULL_TREE, arg);
+         return build_function_call_expr (expfn, arglist);
+       }
 
-      if (powfn)
-       {
-         tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
-         tree tree_root;
-         REAL_VALUE_TYPE dconstroot = dconstthird;
+      /* Optimize cbrt(sqrt(x)) -> pow(x,1/6).  */
+      if (BUILTIN_SQRT_P (fcode))
+        {
+         tree powfn = mathfn_built_in (type, BUILT_IN_POW);
 
-         SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
-         dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
-         tree_root = build_real (type, dconstroot);
-         arglist = tree_cons (NULL_TREE, arg0,
-                              build_tree_list (NULL_TREE, tree_root));
-         return build_function_call_expr (powfn, arglist);
+         if (powfn)
+           {
+             tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+             tree tree_root;
+             REAL_VALUE_TYPE dconstroot = dconstthird;
+
+             SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
+             dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
+             tree_root = build_real (type, dconstroot);
+             arglist = tree_cons (NULL_TREE, arg0,
+                                  build_tree_list (NULL_TREE, tree_root));
+             return build_function_call_expr (powfn, arglist);
+           }
        }
 
+      /* Optimize cbrt(cbrt(x)) -> pow(x,1/9) iff x is nonnegative.  */
+      if (BUILTIN_CBRT_P (fcode))
+        {
+         tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+         if (tree_expr_nonnegative_p (arg0))
+           {
+             tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+
+             if (powfn)
+               {
+                 tree tree_root;
+                 REAL_VALUE_TYPE dconstroot;
+             
+                 real_arithmetic (&dconstroot, MULT_EXPR, &dconstthird, &dconstthird);
+                 dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
+                 tree_root = build_real (type, dconstroot);
+                 arglist = tree_cons (NULL_TREE, arg0,
+                                      build_tree_list (NULL_TREE, tree_root));
+                 return build_function_call_expr (powfn, arglist);
+               }
+           }
+       }
+      
+      /* Optimize cbrt(pow(x,y)) -> pow(x,y/3) iff x is nonnegative.  */
+      if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF
+         || fcode == BUILT_IN_POWL)
+        {
+         tree arg00 = TREE_VALUE (TREE_OPERAND (arg, 1));
+         tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
+         if (tree_expr_nonnegative_p (arg00))
+           {
+             tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+             const REAL_VALUE_TYPE dconstroot
+               = real_value_truncate (TYPE_MODE (type), dconstthird);
+             tree narg01 = fold (build2 (MULT_EXPR, type, arg01,
+                                         build_real (type, dconstroot)));
+             arglist = tree_cons (NULL_TREE, arg00,
+                                  build_tree_list (NULL_TREE, narg01));
+             return build_function_call_expr (powfn, arglist);
+           }
+       }
     }
   return NULL_TREE;
 }
@@ -6431,9 +7002,8 @@ fold_builtin_atan (tree arglist, tree type)
    NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_trunc (tree exp)
+fold_builtin_trunc (tree fndecl, tree arglist)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree arg;
 
   if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
@@ -6444,23 +7014,22 @@ fold_builtin_trunc (tree exp)
   if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
     {
       REAL_VALUE_TYPE r, x;
-      tree type = TREE_TYPE (exp);
+      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 (exp);
+  return fold_trunc_transparent_mathfn (fndecl, arglist);
 }
 
 /* Fold function call to builtin floor, floorf or floorl.  Return
    NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_floor (tree exp)
+fold_builtin_floor (tree fndecl, tree arglist)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree arg;
 
   if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
@@ -6475,7 +7044,7 @@ fold_builtin_floor (tree exp)
       x = TREE_REAL_CST (arg);
       if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
        {
-         tree type = TREE_TYPE (exp);
+         tree type = TREE_TYPE (TREE_TYPE (fndecl));
          REAL_VALUE_TYPE r;
 
          real_floor (&r, TYPE_MODE (type), &x);
@@ -6483,16 +7052,15 @@ fold_builtin_floor (tree exp)
        }
     }
 
-  return fold_trunc_transparent_mathfn (exp);
+  return fold_trunc_transparent_mathfn (fndecl, arglist);
 }
 
 /* Fold function call to builtin ceil, ceilf or ceill.  Return
    NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_ceil (tree exp)
+fold_builtin_ceil (tree fndecl, tree arglist)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree arg;
 
   if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
@@ -6507,7 +7075,7 @@ fold_builtin_ceil (tree exp)
       x = TREE_REAL_CST (arg);
       if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
        {
-         tree type = TREE_TYPE (exp);
+         tree type = TREE_TYPE (TREE_TYPE (fndecl));
          REAL_VALUE_TYPE r;
 
          real_ceil (&r, TYPE_MODE (type), &x);
@@ -6515,16 +7083,15 @@ fold_builtin_ceil (tree exp)
        }
     }
 
-  return fold_trunc_transparent_mathfn (exp);
+  return fold_trunc_transparent_mathfn (fndecl, arglist);
 }
 
 /* Fold function call to builtin round, roundf or roundl.  Return
    NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_round (tree exp)
+fold_builtin_round (tree fndecl, tree arglist)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree arg;
 
   if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
@@ -6539,7 +7106,7 @@ fold_builtin_round (tree exp)
       x = TREE_REAL_CST (arg);
       if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
        {
-         tree type = TREE_TYPE (exp);
+         tree type = TREE_TYPE (TREE_TYPE (fndecl));
          REAL_VALUE_TYPE r;
 
          real_round (&r, TYPE_MODE (type), &x);
@@ -6547,17 +7114,16 @@ fold_builtin_round (tree exp)
        }
     }
 
-  return fold_trunc_transparent_mathfn (exp);
+  return fold_trunc_transparent_mathfn (fndecl, arglist);
 }
 
 /* Fold function call to builtin lround, lroundf or lroundl (or the
-   corresponding long long versions).  Return NULL_TREE if no
-   simplification can be made.  */
+   corresponding long long versions) and other rounding functions.
+   Return NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_lround (tree exp)
+fold_builtin_int_roundingfn (tree fndecl, tree arglist)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree arg;
 
   if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
@@ -6571,11 +7137,44 @@ fold_builtin_lround (tree exp)
 
       if (! REAL_VALUE_ISNAN (x) && ! REAL_VALUE_ISINF (x))
        {
-         tree itype = TREE_TYPE (exp), ftype = TREE_TYPE (arg), result;
+         tree itype = TREE_TYPE (TREE_TYPE (fndecl));
+         tree ftype = TREE_TYPE (arg), result;
          HOST_WIDE_INT hi, lo;
          REAL_VALUE_TYPE r;
 
-         real_round (&r, TYPE_MODE (ftype), &x);
+         switch (DECL_FUNCTION_CODE (fndecl))
+           {
+           case BUILT_IN_LFLOOR:
+           case BUILT_IN_LFLOORF:
+           case BUILT_IN_LFLOORL:
+           case BUILT_IN_LLFLOOR:
+           case BUILT_IN_LLFLOORF:
+           case BUILT_IN_LLFLOORL:
+             real_floor (&r, TYPE_MODE (ftype), &x);
+             break;
+
+           case BUILT_IN_LCEIL:
+           case BUILT_IN_LCEILF:
+           case BUILT_IN_LCEILL:
+           case BUILT_IN_LLCEIL:
+           case BUILT_IN_LLCEILF:
+           case BUILT_IN_LLCEILL:
+             real_ceil (&r, TYPE_MODE (ftype), &x);
+             break;
+
+           case BUILT_IN_LROUND:
+           case BUILT_IN_LROUNDF:
+           case BUILT_IN_LROUNDL:
+           case BUILT_IN_LLROUND:
+           case BUILT_IN_LLROUNDF:
+           case BUILT_IN_LLROUNDL:
+             real_round (&r, TYPE_MODE (ftype), &x);
+             break;
+
+           default:
+             gcc_unreachable ();
+           }
+
          REAL_VALUE_TO_INT (&lo, &hi, r);
          result = build_int_cst_wide (NULL_TREE, lo, hi);
          if (int_fits_type_p (result, itype))
@@ -6583,7 +7182,7 @@ fold_builtin_lround (tree exp)
        }
     }
 
-  return fold_fixed_mathfn (exp);
+  return fold_fixed_mathfn (fndecl, arglist);
 }
 
 /* Fold function call to builtin ffs, clz, ctz, popcount and parity
@@ -6591,10 +7190,8 @@ fold_builtin_lround (tree exp)
    Return NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_bitop (tree exp)
+fold_builtin_bitop (tree fndecl, tree arglist)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  tree arglist = TREE_OPERAND (exp, 1);
   tree arg;
 
   if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
@@ -6686,7 +7283,7 @@ fold_builtin_bitop (tree exp)
          gcc_unreachable ();
        }
 
-      return build_int_cst (TREE_TYPE (exp), result);
+      return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), result);
     }
 
   return NULL_TREE;
@@ -6712,13 +7309,11 @@ real_dconstp (tree expr, const REAL_VALUE_TYPE *value)
    function.  VALUE is the base of the logN function.  */
 
 static tree
-fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value)
+fold_builtin_logarithm (tree fndecl, tree arglist,
+                       const REAL_VALUE_TYPE *value)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
-
   if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
     {
-      tree fndecl = get_callee_fndecl (exp);
       tree type = TREE_TYPE (TREE_TYPE (fndecl));
       tree arg = TREE_VALUE (arglist);
       const enum built_in_function fcode = builtin_mathfn_code (arg);
@@ -6830,7 +7425,6 @@ fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value)
 static tree
 fold_builtin_pow (tree fndecl, tree arglist, tree type)
 {
-  enum built_in_function fcode;
   tree arg0 = TREE_VALUE (arglist);
   tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
 
@@ -6844,7 +7438,10 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type)
   if (TREE_CODE (arg1) == REAL_CST
       && ! TREE_CONSTANT_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.  */
@@ -6874,17 +7471,14 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type)
            }
        }
 
-      /* Attempt to evaluate pow at compile-time.  */
-      if (TREE_CODE (arg0) == REAL_CST
-         && ! TREE_CONSTANT_OVERFLOW (arg0))
+      /* Check for an integer exponent.  */
+      n = real_to_integer (&c);
+      real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
+      if (real_identical (&c, &cint))
        {
-         REAL_VALUE_TYPE cint;
-         HOST_WIDE_INT n;
-
-         n = real_to_integer (&c);
-         real_from_integer (&cint, VOIDmode, n,
-                            n < 0 ? -1 : 0, 0);
-         if (real_identical (&c, &cint))
+         /* Attempt to evaluate pow at compile-time.  */
+         if (TREE_CODE (arg0) == REAL_CST
+             && ! TREE_CONSTANT_OVERFLOW (arg0))
            {
              REAL_VALUE_TYPE x;
              bool inexact;
@@ -6894,45 +7488,123 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type)
              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)
+               {
+                 arglist = build_tree_list (NULL_TREE, arg1);
+                 arglist = tree_cons (NULL_TREE, narg0, arglist);
+                 return build_function_call_expr (fndecl, arglist);
+               }
+           }
        }
     }
 
-  /* Optimize pow(expN(x),y) = expN(x*y).  */
-  fcode = builtin_mathfn_code (arg0);
-  if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
+  if (flag_unsafe_math_optimizations)
     {
-      tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
-      tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
-      arg = fold (build2 (MULT_EXPR, type, arg, arg1));
-      arglist = build_tree_list (NULL_TREE, arg);
-      return build_function_call_expr (expfn, arglist);
-    }
+      const enum built_in_function fcode = builtin_mathfn_code (arg0);
 
-  /* Optimize pow(sqrt(x),y) = pow(x,y*0.5).  */
-  if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode))
-    {
-      tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1));
-      tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
-                                build_real (type, dconsthalf)));
+      /* Optimize pow(expN(x),y) = expN(x*y).  */
+      if (BUILTIN_EXPONENT_P (fcode))
+        {
+         tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
+         tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
+         arg = fold (build2 (MULT_EXPR, type, arg, arg1));
+         arglist = build_tree_list (NULL_TREE, arg);
+         return build_function_call_expr (expfn, arglist);
+       }
 
-      arglist = tree_cons (NULL_TREE, narg0,
-                          build_tree_list (NULL_TREE, narg1));
-      return build_function_call_expr (fndecl, arglist);
+      /* Optimize pow(sqrt(x),y) = pow(x,y*0.5).  */
+      if (BUILTIN_SQRT_P (fcode))
+        {
+         tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1));
+         tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
+                                    build_real (type, dconsthalf)));
+
+         arglist = tree_cons (NULL_TREE, narg0,
+                              build_tree_list (NULL_TREE, narg1));
+         return build_function_call_expr (fndecl, arglist);
+       }
+
+      /* Optimize pow(cbrt(x),y) = pow(x,y/3) iff x is nonnegative.  */
+      if (BUILTIN_CBRT_P (fcode))
+        {
+         tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
+         if (tree_expr_nonnegative_p (arg))
+           {
+             const REAL_VALUE_TYPE dconstroot
+               = real_value_truncate (TYPE_MODE (type), dconstthird);
+             tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
+                                        build_real (type, dconstroot)));
+             arglist = tree_cons (NULL_TREE, arg,
+                                  build_tree_list (NULL_TREE, narg1));
+             return build_function_call_expr (fndecl, arglist);
+           }
+       }
+      
+      /* Optimize pow(pow(x,y),z) = pow(x,y*z).  */
+      if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF
+          || fcode == BUILT_IN_POWL)
+        {
+         tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1));
+         tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1)));
+         tree narg1 = fold (build2 (MULT_EXPR, type, arg01, arg1));
+         arglist = tree_cons (NULL_TREE, arg00,
+                              build_tree_list (NULL_TREE, narg1));
+         return build_function_call_expr (fndecl, arglist);
+       }
     }
 
-  /* Optimize pow(pow(x,y),z) = pow(x,y*z).  */
-  if (flag_unsafe_math_optimizations
-      && (fcode == BUILT_IN_POW
-         || fcode == BUILT_IN_POWF
-         || fcode == BUILT_IN_POWL))
+  return NULL_TREE;
+}
+
+/* Fold a builtin function call to powi, powif, or powil.  Return
+   NULL_TREE if no simplification can be made.  */
+static tree
+fold_builtin_powi (tree fndecl ATTRIBUTE_UNUSED, tree arglist, tree type)
+{
+  tree arg0 = TREE_VALUE (arglist);
+  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+  if (!validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_TREE;
+
+  /* Optimize pow(1.0,y) = 1.0.  */
+  if (real_onep (arg0))
+    return omit_one_operand (type, build_real (type, dconst1), arg1);
+
+  if (host_integerp (arg1, 0))
     {
-      tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1));
-      tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1)));
-      tree narg1 = fold (build2 (MULT_EXPR, type, arg01, arg1));
-      arglist = tree_cons (NULL_TREE, arg00,
-                          build_tree_list (NULL_TREE, narg1));
-      return build_function_call_expr (fndecl, arglist);
+      HOST_WIDE_INT c = TREE_INT_CST_LOW (arg1);
+
+      /* Evaluate powi at compile-time.  */
+      if (TREE_CODE (arg0) == REAL_CST
+         && ! TREE_CONSTANT_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 (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 (RDIV_EXPR, type,
+                            build_real (type, dconst1), arg0));
     }
+
   return NULL_TREE;
 }
 
@@ -6941,13 +7613,11 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type)
    VALUE is the value which will be raised to a power.  */
 
 static tree
-fold_builtin_exponent (tree exp, const REAL_VALUE_TYPE *value)
+fold_builtin_exponent (tree fndecl, tree arglist,
+                      const REAL_VALUE_TYPE *value)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
-
   if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
     {
-      tree fndecl = get_callee_fndecl (exp);
       tree type = TREE_TYPE (TREE_TYPE (fndecl));
       tree arg = TREE_VALUE (arglist);
 
@@ -7014,9 +7684,8 @@ fold_builtin_exponent (tree exp, const REAL_VALUE_TYPE *value)
    NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_memcpy (tree exp)
+fold_builtin_memcpy (tree fndecl, tree arglist)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree dest, src, len;
 
   if (!validate_arglist (arglist,
@@ -7029,11 +7698,11 @@ fold_builtin_memcpy (tree exp)
 
   /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
-    return omit_one_operand (TREE_TYPE (exp), dest, src);
+    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
-    return omit_one_operand (TREE_TYPE (exp), dest, len);
+    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len);
 
   return 0;
 }
@@ -7105,9 +7774,8 @@ fold_builtin_memmove (tree arglist, tree type)
    simplification can be made.  */
 
 tree
-fold_builtin_strcpy (tree exp, tree len)
+fold_builtin_strcpy (tree fndecl, tree arglist, tree len)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree dest, src, fn;
 
   if (!validate_arglist (arglist,
@@ -7119,7 +7787,7 @@ fold_builtin_strcpy (tree exp, tree len)
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
-    return fold_convert (TREE_TYPE (exp), dest);
+    return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest);
 
   if (optimize_size)
     return 0;
@@ -7139,7 +7807,7 @@ fold_builtin_strcpy (tree exp, tree len)
   arglist = build_tree_list (NULL_TREE, len);
   arglist = tree_cons (NULL_TREE, src, arglist);
   arglist = tree_cons (NULL_TREE, dest, arglist);
-  return fold_convert (TREE_TYPE (exp),
+  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
                       build_function_call_expr (fn, arglist));
 }
 
@@ -7148,9 +7816,8 @@ fold_builtin_strcpy (tree exp, tree len)
    can be made.  */
 
 tree
-fold_builtin_strncpy (tree exp, tree slen)
+fold_builtin_strncpy (tree fndecl, tree arglist, tree slen)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
   tree dest, src, len, fn;
 
   if (!validate_arglist (arglist,
@@ -7163,7 +7830,7 @@ fold_builtin_strncpy (tree exp, tree slen)
 
   /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
-    return omit_one_operand (TREE_TYPE (exp), dest, src);
+    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
 
   /* We can't compare slen with len as constants below if len is not a
      constant.  */
@@ -7189,7 +7856,7 @@ fold_builtin_strncpy (tree exp, tree slen)
   fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
   if (!fn)
     return 0;
-  return fold_convert (TREE_TYPE (exp),
+  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
                       build_function_call_expr (fn, arglist));
 }
 
@@ -7410,9 +8077,9 @@ fold_builtin_strncmp (tree arglist)
    NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_signbit (tree exp)
+fold_builtin_signbit (tree fndecl, tree arglist)
 {
-  tree arglist = TREE_OPERAND (exp, 1);
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
   tree arg, temp;
 
   if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
@@ -7428,16 +8095,16 @@ fold_builtin_signbit (tree exp)
 
       c = TREE_REAL_CST (arg);
       temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node;
-      return fold_convert (TREE_TYPE (exp), temp);
+      return fold_convert (type, temp);
     }
 
   /* If ARG is non-negative, the result is always zero.  */
   if (tree_expr_nonnegative_p (arg))
-    return omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg);
+    return omit_one_operand (type, integer_zero_node, arg);
 
   /* If ARG's format doesn't have signed zeros, return "arg < 0.0".  */
   if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg))))
-    return fold (build2 (LT_EXPR, TREE_TYPE (exp), arg,
+    return fold (build2 (LT_EXPR, type, arg,
                         build_real (TREE_TYPE (arg), dconst0)));
 
   return NULL_TREE;
@@ -7447,9 +8114,9 @@ fold_builtin_signbit (tree exp)
    Return NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_copysign (tree arglist, tree type)
+fold_builtin_copysign (tree fndecl, tree arglist, tree type)
 {
-  tree arg1, arg2;
+  tree arg1, arg2, tem;
 
   if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
     return NULL_TREE;
@@ -7483,6 +8150,14 @@ fold_builtin_copysign (tree arglist, tree type)
                             fold (build1 (ABS_EXPR, type, arg1)),
                             arg2);
 
+  /* Strip sign changing operations for the first argument.  */
+  tem = fold_strip_sign_ops (arg1);
+  if (tem)
+    {
+      arglist = tree_cons (NULL_TREE, tem, TREE_CHAIN (arglist));
+      return build_function_call_expr (fndecl, arglist);
+    }
+
   return NULL_TREE;
 }
 
@@ -7538,11 +8213,18 @@ fold_builtin_isdigit (tree arglist)
   else
     {
       /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9.  */
-      /* According to the C standard, isdigit is unaffected by locale.  */
-      tree arg = TREE_VALUE (arglist);
-      arg = fold_convert (unsigned_type_node, arg);
+      /* According to the C standard, isdigit is unaffected by locale.
+        However, it definitely is affected by the target character set.  */
+      tree arg;
+      unsigned HOST_WIDE_INT target_digit0
+       = lang_hooks.to_target_charset ('0');
+
+      if (target_digit0 == 0)
+       return NULL_TREE;
+
+      arg = fold_convert (unsigned_type_node, TREE_VALUE (arglist));
       arg = build2 (MINUS_EXPR, unsigned_type_node, arg,
-                   build_int_cst (unsigned_type_node, TARGET_DIGIT0));
+                   build_int_cst (unsigned_type_node, target_digit0));
       arg = build2 (LE_EXPR, integer_type_node, arg,
                    build_int_cst (unsigned_type_node, 9));
       arg = fold (arg);
@@ -7591,10 +8273,8 @@ fold_builtin_abs (tree arglist, tree type)
    EXP is the CALL_EXPR for the call.  */
 
 static tree
-fold_builtin_classify (tree exp, int builtin_index)
+fold_builtin_classify (tree fndecl, tree arglist, int builtin_index)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  tree arglist = TREE_OPERAND (exp, 1);
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   tree arg;
   REAL_VALUE_TYPE r;
@@ -7674,19 +8354,18 @@ fold_builtin_classify (tree exp, int builtin_index)
 }
 
 /* Fold a call to an unordered comparison function such as
-   __builtin_isgreater().  EXP is the CALL_EXPR for the call.
+   __builtin_isgreater().  FNDECL is the FUNCTION_DECL for the function
+   being called and ARGLIST is the argument list for the call.
    UNORDERED_CODE and ORDERED_CODE are comparison codes that give
    the opposite of the desired result.  UNORDERED_CODE is used
    for modes that can hold NaNs and ORDERED_CODE is used for
    the rest.  */
 
 static tree
-fold_builtin_unordered_cmp (tree exp,
+fold_builtin_unordered_cmp (tree fndecl, tree arglist,
                            enum tree_code unordered_code,
                            enum tree_code ordered_code)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  tree arglist = TREE_OPERAND (exp, 1);
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum tree_code code;
   tree arg0, arg1;
@@ -7751,22 +8430,60 @@ fold_builtin_unordered_cmp (tree exp,
                       fold (build2 (code, type, arg0, arg1))));
 }
 
+/* Fold a call to one of the external complex multiply libcalls.  */
+
+static tree
+fold_builtin_complex_mul (tree type, tree arglist)
+{
+  tree ar, ai, br, bi;
+
+  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE,
+                        REAL_TYPE, VOID_TYPE))
+    return NULL;
+
+  ar = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+  ai = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+  br = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+  bi = TREE_VALUE (arglist);
+
+  return fold_complex_mult_parts (type, ar, ai, br, bi);
+}
+
+/* Fold a call to one of the external complex division libcalls.  */
+
+static tree
+fold_builtin_complex_div (tree type, tree arglist)
+{
+  tree ar, ai, br, bi;
+
+  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE,
+                        REAL_TYPE, VOID_TYPE))
+    return NULL;
+
+  ar = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+  ai = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+  br = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+  bi = TREE_VALUE (arglist);
+
+  return fold_complex_div_parts (type, ar, ai, br, bi, RDIV_EXPR);
+}
+
 /* Used by constant folding to simplify calls to builtin functions.  EXP is
    the CALL_EXPR of a call to a builtin function.  IGNORE is true if the
    result of the function call is ignored.  This function returns NULL_TREE
    if no simplification was possible.  */
 
 static tree
-fold_builtin_1 (tree exp, bool ignore)
+fold_builtin_1 (tree fndecl, tree arglist, bool ignore)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  tree arglist = TREE_OPERAND (exp, 1);
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  enum built_in_function fcode;
 
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
-    return targetm.fold_builtin (exp, ignore);
+    return targetm.fold_builtin (fndecl, arglist, ignore);
 
-  switch (DECL_FUNCTION_CODE (fndecl))
+  fcode = DECL_FUNCTION_CODE (fndecl);
+  switch (fcode)
     {
     case BUILT_IN_FPUTS:
       return fold_builtin_fputs (arglist, ignore, false, NULL_TREE);
@@ -7775,7 +8492,7 @@ fold_builtin_1 (tree exp, bool ignore)
       return fold_builtin_fputs (arglist, ignore, true, NULL_TREE);
 
     case BUILT_IN_STRSTR:
-      return fold_builtin_strstr (arglist);
+      return fold_builtin_strstr (arglist, type);
 
     case BUILT_IN_STRCAT:
       return fold_builtin_strcat (arglist);
@@ -7791,17 +8508,17 @@ fold_builtin_1 (tree exp, bool ignore)
 
     case BUILT_IN_STRCHR:
     case BUILT_IN_INDEX:
-      return fold_builtin_strchr (arglist);
+      return fold_builtin_strchr (arglist, type);
 
     case BUILT_IN_STRRCHR:
     case BUILT_IN_RINDEX:
-      return fold_builtin_strrchr (arglist);
+      return fold_builtin_strrchr (arglist, type);
 
     case BUILT_IN_STRCPY:
-      return fold_builtin_strcpy (exp, NULL_TREE);
+      return fold_builtin_strcpy (fndecl, arglist, NULL_TREE);
 
     case BUILT_IN_STRNCPY:
-      return fold_builtin_strncpy (exp, NULL_TREE);
+      return fold_builtin_strncpy (fndecl, arglist, NULL_TREE);
 
     case BUILT_IN_STRCMP:
       return fold_builtin_strcmp (arglist);
@@ -7810,7 +8527,7 @@ fold_builtin_1 (tree exp, bool ignore)
       return fold_builtin_strncmp (arglist);
 
     case BUILT_IN_STRPBRK:
-      return fold_builtin_strpbrk (arglist);
+      return fold_builtin_strpbrk (arglist, type);
 
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
@@ -7904,12 +8621,12 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_EXP:
     case BUILT_IN_EXPF:
     case BUILT_IN_EXPL:
-      return fold_builtin_exponent (exp, &dconste);
+      return fold_builtin_exponent (fndecl, arglist, &dconste);
 
     case BUILT_IN_EXP2:
     case BUILT_IN_EXP2F:
     case BUILT_IN_EXP2L:
-      return fold_builtin_exponent (exp, &dconst2);
+      return fold_builtin_exponent (fndecl, arglist, &dconst2);
 
     case BUILT_IN_EXP10:
     case BUILT_IN_EXP10F:
@@ -7917,22 +8634,22 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_POW10:
     case BUILT_IN_POW10F:
     case BUILT_IN_POW10L:
-      return fold_builtin_exponent (exp, &dconst10);
+      return fold_builtin_exponent (fndecl, arglist, &dconst10);
 
     case BUILT_IN_LOG:
     case BUILT_IN_LOGF:
     case BUILT_IN_LOGL:
-      return fold_builtin_logarithm (exp, &dconste);
+      return fold_builtin_logarithm (fndecl, arglist, &dconste);
 
     case BUILT_IN_LOG2:
     case BUILT_IN_LOG2F:
     case BUILT_IN_LOG2L:
-      return fold_builtin_logarithm (exp, &dconst2);
+      return fold_builtin_logarithm (fndecl, arglist, &dconst2);
 
     case BUILT_IN_LOG10:
     case BUILT_IN_LOG10F:
     case BUILT_IN_LOG10L:
-      return fold_builtin_logarithm (exp, &dconst10);
+      return fold_builtin_logarithm (fndecl, arglist, &dconst10);
 
     case BUILT_IN_TAN:
     case BUILT_IN_TANF:
@@ -7949,6 +8666,11 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_POWL:
       return fold_builtin_pow (fndecl, arglist, type);
 
+    case BUILT_IN_POWI:
+    case BUILT_IN_POWIF:
+    case BUILT_IN_POWIL:
+      return fold_builtin_powi (fndecl, arglist, type);
+
     case BUILT_IN_INF:
     case BUILT_IN_INFF:
     case BUILT_IN_INFL:
@@ -7972,22 +8694,22 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_FLOOR:
     case BUILT_IN_FLOORF:
     case BUILT_IN_FLOORL:
-      return fold_builtin_floor (exp);
+      return fold_builtin_floor (fndecl, arglist);
 
     case BUILT_IN_CEIL:
     case BUILT_IN_CEILF:
     case BUILT_IN_CEILL:
-      return fold_builtin_ceil (exp);
+      return fold_builtin_ceil (fndecl, arglist);
 
     case BUILT_IN_TRUNC:
     case BUILT_IN_TRUNCF:
     case BUILT_IN_TRUNCL:
-      return fold_builtin_trunc (exp);
+      return fold_builtin_trunc (fndecl, arglist);
 
     case BUILT_IN_ROUND:
     case BUILT_IN_ROUNDF:
     case BUILT_IN_ROUNDL:
-      return fold_builtin_round (exp);
+      return fold_builtin_round (fndecl, arglist);
 
     case BUILT_IN_NEARBYINT:
     case BUILT_IN_NEARBYINTF:
@@ -7995,15 +8717,27 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_RINT:
     case BUILT_IN_RINTF:
     case BUILT_IN_RINTL:
-      return fold_trunc_transparent_mathfn (exp);
-
+      return fold_trunc_transparent_mathfn (fndecl, arglist);
+
+    case BUILT_IN_LCEIL:
+    case BUILT_IN_LCEILF:
+    case BUILT_IN_LCEILL:
+    case BUILT_IN_LLCEIL:
+    case BUILT_IN_LLCEILF:
+    case BUILT_IN_LLCEILL:
+    case BUILT_IN_LFLOOR:
+    case BUILT_IN_LFLOORF:
+    case BUILT_IN_LFLOORL:
+    case BUILT_IN_LLFLOOR:
+    case BUILT_IN_LLFLOORF:
+    case BUILT_IN_LLFLOORL:
     case BUILT_IN_LROUND:
     case BUILT_IN_LROUNDF:
     case BUILT_IN_LROUNDL:
     case BUILT_IN_LLROUND:
     case BUILT_IN_LLROUNDF:
     case BUILT_IN_LLROUNDL:
-      return fold_builtin_lround (exp);
+      return fold_builtin_int_roundingfn (fndecl, arglist);
 
     case BUILT_IN_LRINT:
     case BUILT_IN_LRINTF:
@@ -8011,7 +8745,7 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_LLRINT:
     case BUILT_IN_LLRINTF:
     case BUILT_IN_LLRINTL:
-      return fold_fixed_mathfn (exp);
+      return fold_fixed_mathfn (fndecl, arglist);
 
     case BUILT_IN_FFS:
     case BUILT_IN_FFSL:
@@ -8028,10 +8762,10 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_PARITY:
     case BUILT_IN_PARITYL:
     case BUILT_IN_PARITYLL:
-      return fold_builtin_bitop (exp);
+      return fold_builtin_bitop (fndecl, arglist);
 
     case BUILT_IN_MEMCPY:
-      return fold_builtin_memcpy (exp);
+      return fold_builtin_memcpy (fndecl, arglist);
 
     case BUILT_IN_MEMPCPY:
       return fold_builtin_mempcpy (arglist, type, /*endp=*/1);
@@ -8042,7 +8776,7 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_SIGNBIT:
     case BUILT_IN_SIGNBITF:
     case BUILT_IN_SIGNBITL:
-      return fold_builtin_signbit (exp);
+      return fold_builtin_signbit (fndecl, arglist);
 
     case BUILT_IN_ISASCII:
       return fold_builtin_isascii (arglist);
@@ -8056,41 +8790,48 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_COPYSIGN:
     case BUILT_IN_COPYSIGNF:
     case BUILT_IN_COPYSIGNL:
-      return fold_builtin_copysign (arglist, type);
+      return fold_builtin_copysign (fndecl, arglist, type);
 
     case BUILT_IN_FINITE:
     case BUILT_IN_FINITEF:
     case BUILT_IN_FINITEL:
-      return fold_builtin_classify (exp, BUILT_IN_FINITE);
+      return fold_builtin_classify (fndecl, arglist, BUILT_IN_FINITE);
 
     case BUILT_IN_ISINF:
     case BUILT_IN_ISINFF:
     case BUILT_IN_ISINFL:
-      return fold_builtin_classify (exp, BUILT_IN_ISINF);
+      return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISINF);
 
     case BUILT_IN_ISNAN:
     case BUILT_IN_ISNANF:
     case BUILT_IN_ISNANL:
-      return fold_builtin_classify (exp, BUILT_IN_ISNAN);
+      return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISNAN);
 
     case BUILT_IN_ISGREATER:
-      return fold_builtin_unordered_cmp (exp, UNLE_EXPR, LE_EXPR);
+      return fold_builtin_unordered_cmp (fndecl, arglist, UNLE_EXPR, LE_EXPR);
     case BUILT_IN_ISGREATEREQUAL:
-      return fold_builtin_unordered_cmp (exp, UNLT_EXPR, LT_EXPR);
+      return fold_builtin_unordered_cmp (fndecl, arglist, UNLT_EXPR, LT_EXPR);
     case BUILT_IN_ISLESS:
-      return fold_builtin_unordered_cmp (exp, UNGE_EXPR, GE_EXPR);
+      return fold_builtin_unordered_cmp (fndecl, arglist, UNGE_EXPR, GE_EXPR);
     case BUILT_IN_ISLESSEQUAL:
-      return fold_builtin_unordered_cmp (exp, UNGT_EXPR, GT_EXPR);
+      return fold_builtin_unordered_cmp (fndecl, arglist, UNGT_EXPR, GT_EXPR);
     case BUILT_IN_ISLESSGREATER:
-      return fold_builtin_unordered_cmp (exp, UNEQ_EXPR, EQ_EXPR);
+      return fold_builtin_unordered_cmp (fndecl, arglist, UNEQ_EXPR, EQ_EXPR);
     case BUILT_IN_ISUNORDERED:
-      return fold_builtin_unordered_cmp (exp, UNORDERED_EXPR, NOP_EXPR);
+      return fold_builtin_unordered_cmp (fndecl, arglist, UNORDERED_EXPR,
+                                        NOP_EXPR);
 
       /* We do the folding for va_start in the expander.  */
     case BUILT_IN_VA_START:
       break;
 
     default:
+      if (fcode >= BUILT_IN_COMPLEX_MUL_MIN
+         && fcode <= BUILT_IN_COMPLEX_MUL_MAX)
+       return fold_builtin_complex_mul (type, arglist);
+      if (fcode >= BUILT_IN_COMPLEX_DIV_MIN
+         && fcode <= BUILT_IN_COMPLEX_DIV_MAX)
+       return fold_builtin_complex_div (type, arglist);
       break;
     }
 
@@ -8102,9 +8843,9 @@ fold_builtin_1 (tree exp, bool ignore)
    call node earlier than the warning is generated.  */
 
 tree
-fold_builtin (tree exp, bool ignore)
+fold_builtin (tree fndecl, tree arglist, bool ignore)
 {
-  exp = fold_builtin_1 (exp, ignore);
+  tree exp = fold_builtin_1 (fndecl, arglist, ignore);
   if (exp)
     {
       /* ??? Don't clobber shared nodes such as integer_zero_node.  */
@@ -8240,7 +8981,7 @@ readonly_data_expr (tree exp)
    form of the builtin function call.  */
 
 static tree
-fold_builtin_strstr (tree arglist)
+fold_builtin_strstr (tree arglist, tree type)
 {
   if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     return 0;
@@ -8258,13 +8999,15 @@ fold_builtin_strstr (tree arglist)
       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.  */
-         return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
-                              s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+                             s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         return fold_convert (type, tem);
        }
 
       if (p2[0] == '\0')
@@ -8304,7 +9047,7 @@ fold_builtin_strstr (tree arglist)
    form of the builtin function call.  */
 
 static tree
-fold_builtin_strchr (tree arglist)
+fold_builtin_strchr (tree arglist, tree type)
 {
   if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return 0;
@@ -8321,6 +9064,7 @@ fold_builtin_strchr (tree arglist)
        {
          char c;
          const char *r;
+         tree tem;
 
          if (target_char_cast (s2, &c))
            return 0;
@@ -8331,8 +9075,9 @@ fold_builtin_strchr (tree arglist)
            return build_int_cst (TREE_TYPE (s1), 0);
 
          /* Return an offset into the constant string argument.  */
-         return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
-                              s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+                             s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         return fold_convert (type, tem);
        }
       return 0;
     }
@@ -8356,7 +9101,7 @@ fold_builtin_strchr (tree arglist)
    form of the builtin function call.  */
 
 static tree
-fold_builtin_strrchr (tree arglist)
+fold_builtin_strrchr (tree arglist, tree type)
 {
   if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return 0;
@@ -8374,6 +9119,7 @@ fold_builtin_strrchr (tree arglist)
        {
          char c;
          const char *r;
+         tree tem;
 
          if (target_char_cast (s2, &c))
            return 0;
@@ -8384,8 +9130,9 @@ fold_builtin_strrchr (tree arglist)
            return build_int_cst (TREE_TYPE (s1), 0);
 
          /* Return an offset into the constant string argument.  */
-         return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
-                              s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+                             s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         return fold_convert (type, tem);
        }
 
       if (! integer_zerop (s2))
@@ -8418,7 +9165,7 @@ fold_builtin_strrchr (tree arglist)
    form of the builtin function call.  */
 
 static tree
-fold_builtin_strpbrk (tree arglist)
+fold_builtin_strpbrk (tree arglist, tree type)
 {
   if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     return 0;
@@ -8436,13 +9183,15 @@ fold_builtin_strpbrk (tree arglist)
       if (p1 != NULL)
        {
          const char *r = strpbrk (p1, p2);
+         tree tem;
 
          if (r == NULL)
            return build_int_cst (TREE_TYPE (s1), 0);
 
          /* Return an offset into the constant string argument.  */
-         return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
-                              s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+                             s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+         return fold_convert (type, tem);
        }
 
       if (p2[0] == '\0')
@@ -8764,7 +9513,7 @@ fold_builtin_next_arg (tree arglist)
     {
       /* Evidently an out of date version of <stdarg.h>; can't validate
         va_start's second argument, but can still work as intended.  */
-      warning ("%<__builtin_next_arg%> called without an argument");
+      warning (0, "%<__builtin_next_arg%> called without an argument");
       return true;
     }
   /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0)
@@ -8799,7 +9548,7 @@ fold_builtin_next_arg (tree arglist)
             argument.  We just warn and set the arg to be the last
             argument so that we will get wrong-code because of
             it.  */
-         warning ("second parameter of %<va_start%> not last named argument");
+         warning (0, "second parameter of %<va_start%> not last named argument");
        }
       /* We want to verify the second parameter just once before the tree
          optimizers are run and then avoid keeping it in the tree,