OSDN Git Service

* config/m32r/m32r.md: Use define_constants for unspec and
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 16d483a..2660396 100644 (file)
@@ -79,17 +79,13 @@ tree built_in_decls[(int) END_BUILTINS];
    required to implement the function call in all cases.  */
 tree implicit_built_in_decls[(int) END_BUILTINS];
 
-/* Trigonometric and mathematical constants used in builtin folding.  */
-static bool builtin_dconsts_init = 0;
-static REAL_VALUE_TYPE dconstpi;
-static REAL_VALUE_TYPE dconste;
-
 static int get_pointer_alignment (tree, unsigned int);
-static tree c_strlen (tree);
+static tree c_strlen (tree, int);
 static const char *c_getstr (tree);
 static rtx c_readstr (const char *, enum machine_mode);
 static int target_char_cast (tree, char *);
 static rtx get_memory_rtx (tree);
+static tree build_string_literal (int, const char *);
 static int apply_args_size (void);
 static int apply_result_size (void);
 #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
@@ -140,7 +136,9 @@ static rtx expand_builtin_strrchr (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);
-static rtx expand_builtin_fputs (tree, int, int);
+static rtx expand_builtin_fputs (tree, rtx, bool);
+static rtx expand_builtin_printf (tree, rtx, enum machine_mode, bool);
+static rtx expand_builtin_fprintf (tree, rtx, enum machine_mode, bool);
 static rtx expand_builtin_sprintf (tree, rtx, enum machine_mode);
 static tree stabilize_va_list (tree, int);
 static rtx expand_builtin_expect (tree, rtx);
@@ -149,26 +147,24 @@ static tree fold_builtin_classify_type (tree);
 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 bool readonly_data_expr (tree);
 static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_cabs (tree, rtx);
-static void init_builtin_dconsts (void);
 static tree fold_builtin_cabs (tree, tree, tree);
-
-/* Initialize mathematical constants for constant folding builtins.
-   These constants need to be given to at least 160 bits precision.  */
-
-static void
-init_builtin_dconsts (void)
-{
-  real_from_string (&dconstpi,
-    "3.1415926535897932384626433832795028841971693993751058209749445923078");
-  real_from_string (&dconste,
-    "2.7182818284590452353602874713526624977572470936999595749669676277241");
-
-  builtin_dconsts_init = true;
-}
+static tree fold_builtin_trunc (tree);
+static tree fold_builtin_floor (tree);
+static tree fold_builtin_ceil (tree);
+static tree fold_builtin_bitop (tree);
+static tree fold_builtin_memcpy (tree);
+static tree fold_builtin_mempcpy (tree);
+static tree fold_builtin_memmove (tree);
+static tree fold_builtin_strcpy (tree);
+static tree fold_builtin_strncpy (tree);
+static tree fold_builtin_memcmp (tree);
+static tree fold_builtin_strcmp (tree);
+static tree fold_builtin_strncmp (tree);
 
 /* Return the alignment in bits of EXP, a pointer valued expression.
    But don't return more than MAX_ALIGN no matter what.
@@ -242,19 +238,42 @@ get_pointer_alignment (tree exp, unsigned int max_align)
    way, because it could contain a zero byte in the middle.
    TREE_STRING_LENGTH is the size of the character array, not the string.
 
+   ONLY_VALUE should be nonzero if the result is not going to be emitted
+   into the instruction stream and zero if it is going to be expanded.
+   E.g. with i++ ? "foo" : "bar", if ONLY_VALUE is nonzero, constant 3
+   is returned, otherwise NULL, since
+   len = c_strlen (src, 1); if (len) expand_expr (len, ...); would not
+   evaluate the side-effects.
+
    The value returned is of type `ssizetype'.
 
    Unfortunately, string_constant can't access the values of const char
    arrays with initializers, so neither can we do so here.  */
 
 static tree
-c_strlen (tree src)
+c_strlen (tree src, int only_value)
 {
   tree offset_node;
   HOST_WIDE_INT offset;
   int max;
   const char *ptr;
 
+  STRIP_NOPS (src);
+  if (TREE_CODE (src) == COND_EXPR
+      && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
+    {
+      tree len1, len2;
+
+      len1 = c_strlen (TREE_OPERAND (src, 1), only_value);
+      len2 = c_strlen (TREE_OPERAND (src, 2), only_value);
+      if (tree_int_cst_equal (len1, len2))      
+       return len1;
+    }
+
+  if (TREE_CODE (src) == COMPOUND_EXPR
+      && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
+    return c_strlen (TREE_OPERAND (src, 1), only_value);
+
   src = string_constant (src, &offset_node);
   if (src == 0)
     return 0;
@@ -466,10 +485,7 @@ expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label)
   if (setjmp_alias_set == -1)
     setjmp_alias_set = new_alias_set ();
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (buf_addr) != Pmode)
-    buf_addr = convert_memory_address (Pmode, buf_addr);
-#endif
+  buf_addr = convert_memory_address (Pmode, buf_addr);
 
   buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX));
 
@@ -608,10 +624,11 @@ expand_builtin_setjmp (tree arglist, rtx target)
 
   expand_builtin_setjmp_setup (buf_addr, next_lab);
 
-  /* Set TARGET to zero and branch to the continue label.  */
+  /* Set TARGET to zero and branch to the continue label.  Use emit_jump to
+     ensure that pending stack adjustments are flushed.  */
   emit_move_insn (target, const0_rtx);
-  emit_jump_insn (gen_jump (cont_lab));
-  emit_barrier ();
+  emit_jump (cont_lab);
+
   emit_label (next_lab);
 
   expand_builtin_setjmp_receiver (next_lab);
@@ -649,10 +666,7 @@ expand_builtin_longjmp (rtx buf_addr, rtx value)
   if (setjmp_alias_set == -1)
     setjmp_alias_set = new_alias_set ();
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (buf_addr) != Pmode)
-    buf_addr = convert_memory_address (Pmode, buf_addr);
-#endif
+  buf_addr = convert_memory_address (Pmode, buf_addr);
 
   buf_addr = force_reg (Pmode, buf_addr);
 
@@ -696,6 +710,13 @@ expand_builtin_longjmp (rtx buf_addr, rtx value)
        {
          lab = copy_to_reg (lab);
 
+         emit_insn (gen_rtx_CLOBBER (VOIDmode,
+                                     gen_rtx_MEM (BLKmode,
+                                                  gen_rtx_SCRATCH (VOIDmode))));
+         emit_insn (gen_rtx_CLOBBER (VOIDmode,
+                                     gen_rtx_MEM (BLKmode,
+                                                  hard_frame_pointer_rtx)));
+
          emit_move_insn (hard_frame_pointer_rtx, fp);
          emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX);
 
@@ -793,12 +814,9 @@ expand_builtin_prefetch (tree arglist)
       if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
             (op0,
              insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
-         || (GET_MODE(op0) != Pmode))
+         || (GET_MODE (op0) != Pmode))
        {
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE(op0) != Pmode)
-           op0 = convert_memory_address (Pmode, op0);
-#endif
+         op0 = convert_memory_address (Pmode, op0);
          op0 = force_reg (Pmode, op0);
        }
       emit_insn (gen_prefetch (op0, op1, op2));
@@ -821,10 +839,7 @@ get_memory_rtx (tree exp)
   rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_SUM);
   rtx mem;
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (addr) != Pmode)
-    addr = convert_memory_address (Pmode, addr);
-#endif
+  addr = convert_memory_address (Pmode, addr);
 
   mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
 
@@ -909,7 +924,7 @@ apply_args_size (void)
 
       /* The second value is the structure value address unless this is
         passed as an "invisible" first argument.  */
-      if (struct_value_rtx)
+      if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0))
        size += GET_MODE_SIZE (Pmode);
 
       for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
@@ -1055,7 +1070,7 @@ result_vector (int savep, rtx result)
   int regno, size, align, nelts;
   enum machine_mode mode;
   rtx reg, mem;
-  rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
+  rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
 
   size = nelts = 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
@@ -1081,9 +1096,10 @@ result_vector (int savep, rtx result)
 static rtx
 expand_builtin_apply_args_1 (void)
 {
-  rtx registers;
+  rtx registers, tem;
   int size, align, regno;
   enum machine_mode mode;
+  rtx struct_incoming_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 1);
 
   /* Create a block where the arg-pointer, structure value address,
      and argument registers can be saved.  */
@@ -1091,15 +1107,13 @@ expand_builtin_apply_args_1 (void)
 
   /* Walk past the arg-pointer and structure value address.  */
   size = GET_MODE_SIZE (Pmode);
-  if (struct_value_rtx)
+  if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0))
     size += GET_MODE_SIZE (Pmode);
 
   /* Save each register used in calling a function to the block.  */
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if ((mode = apply_args_mode[regno]) != VOIDmode)
       {
-       rtx tem;
-
        align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
        if (size % align != 0)
          size = CEIL (size, align) * align;
@@ -1111,16 +1125,25 @@ expand_builtin_apply_args_1 (void)
       }
 
   /* Save the arg pointer to the block.  */
-  emit_move_insn (adjust_address (registers, Pmode, 0),
-                 copy_to_reg (virtual_incoming_args_rtx));
+  tem = copy_to_reg (virtual_incoming_args_rtx);
+#ifdef STACK_GROWS_DOWNWARD
+  /* We need the pointer as the caller actually passed them to us, not
+     as we might have pretended they were passed.  Make sure it's a valid
+     operand, as emit_move_insn isn't expected to handle a PLUS.  */
+  tem
+    = force_operand (plus_constant (tem, current_function_pretend_args_size),
+                    NULL_RTX);
+#endif
+  emit_move_insn (adjust_address (registers, Pmode, 0), tem);
+  
   size = GET_MODE_SIZE (Pmode);
 
   /* Save the structure value address unless this is passed as an
      "invisible" first argument.  */
-  if (struct_value_incoming_rtx)
+  if (struct_incoming_value)
     {
       emit_move_insn (adjust_address (registers, Pmode, size),
-                     copy_to_reg (struct_value_incoming_rtx));
+                     copy_to_reg (struct_incoming_value));
       size += GET_MODE_SIZE (Pmode);
     }
 
@@ -1178,11 +1201,9 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
   rtx incoming_args, result, reg, dest, src, call_insn;
   rtx old_stack_level = 0;
   rtx call_fusage = 0;
+  rtx struct_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0);
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (arguments) != Pmode)
-    arguments = convert_memory_address (Pmode, arguments);
-#endif
+  arguments = convert_memory_address (Pmode, arguments);
 
   /* Create a block where the return registers can be saved.  */
   result = assign_stack_local (BLKmode, apply_result_size (), -1);
@@ -1204,7 +1225,7 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
   do_pending_stack_adjust ();
   NO_DEFER_POP;
 
-  /* Save the stack with nonlocal if available */
+  /* Save the stack with nonlocal if available */
 #ifdef HAVE_save_stack_nonlocal
   if (HAVE_save_stack_nonlocal)
     emit_stack_save (SAVE_NONLOCAL, &old_stack_level, NULL_RTX);
@@ -1212,12 +1233,16 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
 #endif
     emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
 
-  /* Push a block of memory onto the stack to store the memory arguments.
-     Save the address in a register, and copy the memory arguments.  ??? I
-     haven't figured out how the calling convention macros effect this,
-     but it's likely that the source and/or destination addresses in
-     the block copy will need updating in machine specific ways.  */
-  dest = allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT);
+  /* Allocate a block of memory onto the stack and copy the memory
+     arguments to the outgoing arguments address.  */
+  allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT);
+  dest = virtual_outgoing_args_rtx;
+#ifndef STACK_GROWS_DOWNWARD
+  if (GET_CODE (argsize) == CONST_INT)
+    dest = plus_constant (dest, -INTVAL (argsize));
+  else
+    dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize));
+#endif
   dest = gen_rtx_MEM (BLKmode, dest);
   set_mem_align (dest, PARM_BOUNDARY);
   src = gen_rtx_MEM (BLKmode, incoming_args);
@@ -1231,7 +1256,7 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
 
   /* Walk past the arg-pointer and structure value address.  */
   size = GET_MODE_SIZE (Pmode);
-  if (struct_value_rtx)
+  if (struct_value)
     size += GET_MODE_SIZE (Pmode);
 
   /* Restore each of the registers previously saved.  Make USE insns
@@ -1251,13 +1276,13 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
   /* Restore the structure value address unless this is passed as an
      "invisible" first argument.  */
   size = GET_MODE_SIZE (Pmode);
-  if (struct_value_rtx)
+  if (struct_value)
     {
       rtx value = gen_reg_rtx (Pmode);
       emit_move_insn (value, adjust_address (arguments, Pmode, size));
-      emit_move_insn (struct_value_rtx, value);
-      if (GET_CODE (struct_value_rtx) == REG)
-       use_reg (&call_fusage, struct_value_rtx);
+      emit_move_insn (struct_value, value);
+      if (GET_CODE (struct_value) == REG)
+       use_reg (&call_fusage, struct_value);
       size += GET_MODE_SIZE (Pmode);
     }
 
@@ -1320,7 +1345,8 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
   OK_DEFER_POP;
 
   /* Return the address of the result block.  */
-  return copy_addr_to_reg (XEXP (result, 0));
+  result = copy_addr_to_reg (XEXP (result, 0));
+  return convert_memory_address (ptr_mode, result);
 }
 
 /* Perform an untyped return.  */
@@ -1333,10 +1359,7 @@ expand_builtin_return (rtx result)
   rtx reg;
   rtx call_fusage = 0;
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (result) != Pmode)
-    result = convert_memory_address (Pmode, result);
-#endif
+  result = convert_memory_address (Pmode, result);
 
   apply_result_size ();
   result = gen_rtx_MEM (BLKmode, result);
@@ -1373,7 +1396,7 @@ expand_builtin_return (rtx result)
 
   /* Return whatever values was restored by jumping directly to the end
      of the function.  */
-  expand_null_return ();
+  expand_naked_return ();
 }
 
 /* Used by expand_builtin_classify_type and fold_builtin_classify_type.  */
@@ -1431,7 +1454,11 @@ expand_builtin_constant_p (tree arglist, enum machine_mode target_mode)
 
   /* We have taken care of the easy cases during constant folding.  This
      case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE
-     get a chance to see if it can deduce whether ARGLIST is constant.  */
+     get a chance to see if it can deduce whether ARGLIST is constant.
+     If CSE isn't going to run, of course, don't bother waiting.  */
+
+  if (cse_not_expected)
+    return const0_rtx;
 
   current_function_calls_constant_p = 1;
 
@@ -1440,211 +1467,111 @@ expand_builtin_constant_p (tree arglist, enum machine_mode target_mode)
   return tmp;
 }
 
-/* Return mathematic function equivalent to FN but operating directly on TYPE,
-   if available.  */
+/* This helper macro, meant to be used in mathfn_built_in below,
+   determines which among a set of three builtin math functions is
+   appropriate for a given type mode.  The `F' and `L' cases are
+   automatically generated from the `double' case.  */
+#define CASE_MATHFN(BUILT_IN_MATHFN) \
+  case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \
+  fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \
+  fcodel = BUILT_IN_MATHFN##L ; break;
+
+/* Return mathematic function equivalent to FN but operating directly
+   on TYPE, if available.  If we can't do the conversion, return zero.  */
 tree
 mathfn_built_in (tree type, enum built_in_function fn)
 {
-  enum built_in_function fcode = NOT_BUILT_IN;
-  if (TYPE_MODE (type) == TYPE_MODE (double_type_node))
-    switch (fn)
-      {
-      case BUILT_IN_SQRT:
-      case BUILT_IN_SQRTF:
-      case BUILT_IN_SQRTL:
-       fcode = BUILT_IN_SQRT;
-       break;
-      case BUILT_IN_SIN:
-      case BUILT_IN_SINF:
-      case BUILT_IN_SINL:
-       fcode = BUILT_IN_SIN;
-       break;
-      case BUILT_IN_COS:
-      case BUILT_IN_COSF:
-      case BUILT_IN_COSL:
-       fcode = BUILT_IN_COS;
-       break;
-      case BUILT_IN_EXP:
-      case BUILT_IN_EXPF:
-      case BUILT_IN_EXPL:
-       fcode = BUILT_IN_EXP;
-       break;
-      case BUILT_IN_LOG:
-      case BUILT_IN_LOGF:
-      case BUILT_IN_LOGL:
-       fcode = BUILT_IN_LOG;
-       break;
-      case BUILT_IN_TAN:
-      case BUILT_IN_TANF:
-      case BUILT_IN_TANL:
-       fcode = BUILT_IN_TAN;
-       break;
-      case BUILT_IN_ATAN:
-      case BUILT_IN_ATANF:
-      case BUILT_IN_ATANL:
-       fcode = BUILT_IN_ATAN;
-       break;
-      case BUILT_IN_FLOOR:
-      case BUILT_IN_FLOORF:
-      case BUILT_IN_FLOORL:
-       fcode = BUILT_IN_FLOOR;
-       break;
-      case BUILT_IN_CEIL:
-      case BUILT_IN_CEILF:
-      case BUILT_IN_CEILL:
-       fcode = BUILT_IN_CEIL;
-       break;
-      case BUILT_IN_TRUNC:
-      case BUILT_IN_TRUNCF:
-      case BUILT_IN_TRUNCL:
-       fcode = BUILT_IN_TRUNC;
-       break;
-      case BUILT_IN_ROUND:
-      case BUILT_IN_ROUNDF:
-      case BUILT_IN_ROUNDL:
-       fcode = BUILT_IN_ROUND;
-       break;
-      case BUILT_IN_NEARBYINT:
-      case BUILT_IN_NEARBYINTF:
-      case BUILT_IN_NEARBYINTL:
-       fcode = BUILT_IN_NEARBYINT;
-       break;
-      default:
-       abort ();
-      }
-  else if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
-    switch (fn)
-      {
-      case BUILT_IN_SQRT:
-      case BUILT_IN_SQRTF:
-      case BUILT_IN_SQRTL:
-       fcode = BUILT_IN_SQRTF;
-       break;
-      case BUILT_IN_SIN:
-      case BUILT_IN_SINF:
-      case BUILT_IN_SINL:
-       fcode = BUILT_IN_SINF;
-       break;
-      case BUILT_IN_COS:
-      case BUILT_IN_COSF:
-      case BUILT_IN_COSL:
-       fcode = BUILT_IN_COSF;
-       break;
-      case BUILT_IN_EXP:
-      case BUILT_IN_EXPF:
-      case BUILT_IN_EXPL:
-       fcode = BUILT_IN_EXPF;
-       break;
-      case BUILT_IN_LOG:
-      case BUILT_IN_LOGF:
-      case BUILT_IN_LOGL:
-       fcode = BUILT_IN_LOGF;
-       break;
-      case BUILT_IN_TAN:
-      case BUILT_IN_TANF:
-      case BUILT_IN_TANL:
-       fcode = BUILT_IN_TANF;
-       break;
-      case BUILT_IN_ATAN:
-      case BUILT_IN_ATANF:
-      case BUILT_IN_ATANL:
-       fcode = BUILT_IN_ATANF;
-       break;
-      case BUILT_IN_FLOOR:
-      case BUILT_IN_FLOORF:
-      case BUILT_IN_FLOORL:
-       fcode = BUILT_IN_FLOORF;
-       break;
-      case BUILT_IN_CEIL:
-      case BUILT_IN_CEILF:
-      case BUILT_IN_CEILL:
-       fcode = BUILT_IN_CEILF;
-       break;
-      case BUILT_IN_TRUNC:
-      case BUILT_IN_TRUNCF:
-      case BUILT_IN_TRUNCL:
-       fcode = BUILT_IN_TRUNCF;
-       break;
-      case BUILT_IN_ROUND:
-      case BUILT_IN_ROUNDF:
-      case BUILT_IN_ROUNDL:
-       fcode = BUILT_IN_ROUNDF;
-       break;
-      case BUILT_IN_NEARBYINT:
-      case BUILT_IN_NEARBYINTF:
-      case BUILT_IN_NEARBYINTL:
-       fcode = BUILT_IN_NEARBYINTF;
-       break;
-      default:
-       abort ();
-      }
-  else if (TYPE_MODE (type) == TYPE_MODE (long_double_type_node))
-    switch (fn)
-      {
-      case BUILT_IN_SQRT:
-      case BUILT_IN_SQRTF:
-      case BUILT_IN_SQRTL:
-       fcode = BUILT_IN_SQRTL;
-       break;
-      case BUILT_IN_SIN:
-      case BUILT_IN_SINF:
-      case BUILT_IN_SINL:
-       fcode = BUILT_IN_SINL;
-       break;
-      case BUILT_IN_COS:
-      case BUILT_IN_COSF:
-      case BUILT_IN_COSL:
-       fcode = BUILT_IN_COSL;
-       break;
-      case BUILT_IN_EXP:
-      case BUILT_IN_EXPF:
-      case BUILT_IN_EXPL:
-       fcode = BUILT_IN_EXPL;
-       break;
-      case BUILT_IN_LOG:
-      case BUILT_IN_LOGF:
-      case BUILT_IN_LOGL:
-       fcode = BUILT_IN_LOGL;
-       break;
-      case BUILT_IN_TAN:
-      case BUILT_IN_TANF:
-      case BUILT_IN_TANL:
-       fcode = BUILT_IN_TANL;
-       break;
-      case BUILT_IN_ATAN:
-      case BUILT_IN_ATANF:
-      case BUILT_IN_ATANL:
-       fcode = BUILT_IN_ATANL;
-       break;
-      case BUILT_IN_FLOOR:
-      case BUILT_IN_FLOORF:
-      case BUILT_IN_FLOORL:
-       fcode = BUILT_IN_FLOORL;
-       break;
-      case BUILT_IN_CEIL:
-      case BUILT_IN_CEILF:
-      case BUILT_IN_CEILL:
-       fcode = BUILT_IN_CEILL;
-       break;
-      case BUILT_IN_TRUNC:
-      case BUILT_IN_TRUNCF:
-      case BUILT_IN_TRUNCL:
-       fcode = BUILT_IN_TRUNCL;
-       break;
-      case BUILT_IN_ROUND:
-      case BUILT_IN_ROUNDF:
-      case BUILT_IN_ROUNDL:
-       fcode = BUILT_IN_ROUNDL;
-       break;
-      case BUILT_IN_NEARBYINT:
-      case BUILT_IN_NEARBYINTF:
-      case BUILT_IN_NEARBYINTL:
-       fcode = BUILT_IN_NEARBYINTL;
-       break;
+  const enum machine_mode type_mode = TYPE_MODE (type);
+  enum built_in_function fcode, fcodef, fcodel;
+
+  switch (fn)
+    {
+      CASE_MATHFN (BUILT_IN_ACOS)
+      CASE_MATHFN (BUILT_IN_ACOSH)
+      CASE_MATHFN (BUILT_IN_ASIN)
+      CASE_MATHFN (BUILT_IN_ASINH)
+      CASE_MATHFN (BUILT_IN_ATAN)
+      CASE_MATHFN (BUILT_IN_ATAN2)
+      CASE_MATHFN (BUILT_IN_ATANH)
+      CASE_MATHFN (BUILT_IN_CBRT)
+      CASE_MATHFN (BUILT_IN_CEIL)
+      CASE_MATHFN (BUILT_IN_COPYSIGN)
+      CASE_MATHFN (BUILT_IN_COS)
+      CASE_MATHFN (BUILT_IN_COSH)
+      CASE_MATHFN (BUILT_IN_DREM)
+      CASE_MATHFN (BUILT_IN_ERF)
+      CASE_MATHFN (BUILT_IN_ERFC)
+      CASE_MATHFN (BUILT_IN_EXP)
+      CASE_MATHFN (BUILT_IN_EXP10)
+      CASE_MATHFN (BUILT_IN_EXP2)
+      CASE_MATHFN (BUILT_IN_EXPM1)
+      CASE_MATHFN (BUILT_IN_FABS)
+      CASE_MATHFN (BUILT_IN_FDIM)
+      CASE_MATHFN (BUILT_IN_FLOOR)
+      CASE_MATHFN (BUILT_IN_FMA)
+      CASE_MATHFN (BUILT_IN_FMAX)
+      CASE_MATHFN (BUILT_IN_FMIN)
+      CASE_MATHFN (BUILT_IN_FMOD)
+      CASE_MATHFN (BUILT_IN_FREXP)
+      CASE_MATHFN (BUILT_IN_GAMMA)
+      CASE_MATHFN (BUILT_IN_HUGE_VAL)
+      CASE_MATHFN (BUILT_IN_HYPOT)
+      CASE_MATHFN (BUILT_IN_ILOGB)
+      CASE_MATHFN (BUILT_IN_INF)
+      CASE_MATHFN (BUILT_IN_J0)
+      CASE_MATHFN (BUILT_IN_J1)
+      CASE_MATHFN (BUILT_IN_JN)
+      CASE_MATHFN (BUILT_IN_LDEXP)
+      CASE_MATHFN (BUILT_IN_LGAMMA)
+      CASE_MATHFN (BUILT_IN_LLRINT)
+      CASE_MATHFN (BUILT_IN_LLROUND)
+      CASE_MATHFN (BUILT_IN_LOG)
+      CASE_MATHFN (BUILT_IN_LOG10)
+      CASE_MATHFN (BUILT_IN_LOG1P)
+      CASE_MATHFN (BUILT_IN_LOG2)
+      CASE_MATHFN (BUILT_IN_LOGB)
+      CASE_MATHFN (BUILT_IN_LRINT)
+      CASE_MATHFN (BUILT_IN_LROUND)
+      CASE_MATHFN (BUILT_IN_MODF)
+      CASE_MATHFN (BUILT_IN_NAN)
+      CASE_MATHFN (BUILT_IN_NANS)
+      CASE_MATHFN (BUILT_IN_NEARBYINT)
+      CASE_MATHFN (BUILT_IN_NEXTAFTER)
+      CASE_MATHFN (BUILT_IN_NEXTTOWARD)
+      CASE_MATHFN (BUILT_IN_POW)
+      CASE_MATHFN (BUILT_IN_POW10)
+      CASE_MATHFN (BUILT_IN_REMAINDER)
+      CASE_MATHFN (BUILT_IN_REMQUO)
+      CASE_MATHFN (BUILT_IN_RINT)
+      CASE_MATHFN (BUILT_IN_ROUND)
+      CASE_MATHFN (BUILT_IN_SCALB)
+      CASE_MATHFN (BUILT_IN_SCALBLN)
+      CASE_MATHFN (BUILT_IN_SCALBN)
+      CASE_MATHFN (BUILT_IN_SIGNIFICAND)
+      CASE_MATHFN (BUILT_IN_SIN)
+      CASE_MATHFN (BUILT_IN_SINCOS)
+      CASE_MATHFN (BUILT_IN_SINH)
+      CASE_MATHFN (BUILT_IN_SQRT)
+      CASE_MATHFN (BUILT_IN_TAN)
+      CASE_MATHFN (BUILT_IN_TANH)
+      CASE_MATHFN (BUILT_IN_TGAMMA)
+      CASE_MATHFN (BUILT_IN_TRUNC)
+      CASE_MATHFN (BUILT_IN_Y0)
+      CASE_MATHFN (BUILT_IN_Y1)
+      CASE_MATHFN (BUILT_IN_YN)
+
       default:
-       abort ();
+       return 0;
       }
-  return implicit_built_in_decls[fcode];
+
+  if (type_mode == TYPE_MODE (double_type_node))
+    return implicit_built_in_decls[fcode];
+  else if (type_mode == TYPE_MODE (float_type_node))
+    return implicit_built_in_decls[fcodef];
+  else if (type_mode == TYPE_MODE (long_double_type_node))
+    return implicit_built_in_decls[fcodel];
+  else
+    return 0;
 }
 
 /* If errno must be maintained, expand the RTL to check if the result,
@@ -1696,12 +1623,12 @@ static rtx
 expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
 {
   optab builtin_optab;
-  rtx op0, insns;
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  rtx op0, insns, before_call;
+  tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   enum machine_mode mode;
   bool errno_set = false;
-  tree arg;
+  tree arg, narg;
 
   if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
     return 0;
@@ -1751,7 +1678,7 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
     case BUILT_IN_TRUNC:
     case BUILT_IN_TRUNCF:
     case BUILT_IN_TRUNCL:
-      builtin_optab = trunc_optab; break;
+      builtin_optab = btrunc_optab; break;
     case BUILT_IN_ROUND:
     case BUILT_IN_ROUNDF:
     case BUILT_IN_ROUNDL:
@@ -1766,56 +1693,94 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
 
   /* Make a suitable register to place result in.  */
   mode = TYPE_MODE (TREE_TYPE (exp));
-  target = gen_reg_rtx (mode);
 
   if (! flag_errno_math || ! HONOR_NANS (mode))
     errno_set = false;
 
-  /* Stabilize and compute the argument.  */
-  if (errno_set)
-    switch (TREE_CODE (arg))
-      {
-      case VAR_DECL:
-      case PARM_DECL:
-      case SAVE_EXPR:
-      case REAL_CST:
-       break;
+  /* 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);
 
-      default:
-       /* Wrap the computation of the argument in a SAVE_EXPR, as we
-          need to expand the argument again in expand_errno_check.  This
-          way, we will not perform side-effects more the once.  */
-       arg = save_expr (arg);
-       arglist = build_tree_list (NULL_TREE, arg);
-       exp = build_function_call_expr (fndecl, arglist);
-       break;
-      }
+      /* 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 = save_expr (arg);
+      if (narg != arg)
+       {
+         arglist = build_tree_list (NULL_TREE, arg);
+         exp = build_function_call_expr (fndecl, arglist);
+       }
 
-  op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+      op0 = expand_expr (arg, subtarget, VOIDmode, 0);
 
-  emit_queue ();
-  start_sequence ();
+      emit_queue ();
+      start_sequence ();
 
-  /* Compute into TARGET.
-     Set TARGET to wherever the result comes back.  */
-  target = expand_unop (mode, builtin_optab, op0, target, 0);
+      /* Compute into TARGET.
+        Set TARGET to wherever the result comes back.  */
+      target = expand_unop (mode, builtin_optab, op0, target, 0);
 
-  /* If we were unable to expand via the builtin, stop the
-     sequence (without outputting the insns) and return 0, causing
-     a call to the library function.  */
-  if (target == 0)
-    {
+      if (target != 0)
+       {
+         if (errno_set)
+           expand_errno_check (exp, target);
+
+         /* Output the entire sequence.  */
+         insns = get_insns ();
+         end_sequence ();
+         emit_insn (insns);
+         return target;
+       }
+
+      /* If we were unable to expand via the builtin, stop the sequence
+        (without outputting the insns) and call to the library function
+        with the stabilized argument list.  */
       end_sequence ();
-      return 0;
     }
 
-  if (errno_set)
-    expand_errno_check (exp, target);
+  before_call = get_last_insn ();
 
-  /* Output the entire sequence.  */
-  insns = get_insns ();
-  end_sequence ();
-  emit_insn (insns);
+  target = expand_call (exp, target, target == const0_rtx);
+
+  /* If this is a sqrt operation and we don't care about errno, try to
+     attach a REG_EQUAL note with a SQRT rtx to the emitted libcall.
+     This allows the semantics of the libcall to be visible to the RTL
+     optimizers.  */
+  if (builtin_optab == sqrt_optab && !errno_set)
+    {
+      /* Search backwards through the insns emitted by expand_call looking
+        for the instruction with the REG_RETVAL note.  */
+      rtx last = get_last_insn ();
+      while (last != before_call)
+       {
+         if (find_reg_note (last, REG_RETVAL, NULL))
+           {
+             rtx note = find_reg_note (last, REG_EQUAL, NULL);
+             /* Check that the REQ_EQUAL note is an EXPR_LIST with
+                two elements, i.e. symbol_ref(sqrt) and the operand.  */
+             if (note
+                 && GET_CODE (note) == EXPR_LIST
+                 && GET_CODE (XEXP (note, 0)) == EXPR_LIST
+                 && XEXP (XEXP (note, 0), 1) != NULL_RTX
+                 && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX)
+               {
+                 rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0);
+                 /* Check operand is a register with expected mode.  */
+                 if (operand
+                     && GET_CODE (operand) == REG
+                     && GET_MODE (operand) == mode)
+                   {
+                     /* Replace the REG_EQUAL note with a SQRT rtx.  */
+                     rtx equiv = gen_rtx_SQRT (mode, operand);
+                     set_unique_reg_note (last, REG_EQUAL, equiv);
+                   }
+               }
+             break;
+           }
+         last = PREV_INSN (last);
+       }
+    }
 
   return target;
 }
@@ -1832,9 +1797,9 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
 {
   optab builtin_optab;
   rtx op0, op1, insns;
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
-  tree arg0, arg1, temp;
+  tree arg0, arg1, temp, narg;
   enum machine_mode mode;
   bool errno_set = true;
   bool stable = true;
@@ -1861,50 +1826,37 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
 
   /* 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)
+    return 0;
+
   target = gen_reg_rtx (mode);
 
   if (! flag_errno_math || ! HONOR_NANS (mode))
     errno_set = false;
 
-  /* Stabilize the arguments.  */
-  if (errno_set)
+  /* Alway stabilize the argument list.  */
+  narg = save_expr (arg1);
+  if (narg != arg1)
     {
-      switch (TREE_CODE (arg1))
-       {
-       case VAR_DECL:
-       case PARM_DECL:
-       case SAVE_EXPR:
-       case REAL_CST:
-         temp = TREE_CHAIN (arglist);
-         break;
-
-       default:
-         stable = false;
-         arg1 = save_expr (arg1);
-         temp = build_tree_list (NULL_TREE, arg1);
-         break;
-        }
-
-      switch (TREE_CODE (arg0))
-       {
-       case VAR_DECL:
-       case PARM_DECL:
-       case SAVE_EXPR:
-       case REAL_CST:
-         if (! stable)
-           arglist = tree_cons (NULL_TREE, arg0, temp);
-         break;
-
-       default:
-         stable = false;
-         arg0 = save_expr (arg0);
-         arglist = tree_cons (NULL_TREE, arg0, temp);
-         break;
-       }
+      temp = build_tree_list (NULL_TREE, narg);
+      stable = false;
+    }
+  else
+    temp = TREE_CHAIN (arglist);
 
-      if (! stable)
-       exp = build_function_call_expr (fndecl, arglist);
+  narg = save_expr (arg0);
+  if (narg != arg0)
+    {
+      arglist = tree_cons (NULL_TREE, narg, temp);
+      stable = false;
     }
+  else if (! stable)
+    arglist = tree_cons (NULL_TREE, arg0, temp);
+
+  if (! stable)
+    exp = build_function_call_expr (fndecl, arglist);
 
   op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
   op1 = expand_expr (arg1, 0, VOIDmode, 0);
@@ -1917,13 +1869,13 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
   target = expand_binop (mode, builtin_optab, op0, op1,
                         target, 0, OPTAB_DIRECT);
 
-  /* If we were unable to expand via the builtin, stop the
-     sequence (without outputting the insns) and return 0, causing
-     a call to the library function.  */
+  /* If we were unable to expand via the builtin, stop the sequence
+     (without outputting the insns) and call to the library function
+     with the stabilized argument list.  */
   if (target == 0)
     {
       end_sequence ();
-      return 0;
+      return expand_call (exp, target, target == const0_rtx);
     }
 
   if (errno_set)
@@ -1937,6 +1889,258 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
   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
+   introduction to exponentiation algorithms and "addition chains",
+   see section 4.6.3, "Evaluation of Powers" of Donald E. Knuth,
+   "Seminumerical Algorithms", Vol. 2, "The Art of Computer Programming",
+   3rd Edition, 1998, and Daniel M. Gordon, "A Survey of Fast Exponentiation
+   Methods", Journal of Algorithms, Vol. 27, pp. 129-146, 1998.  */
+
+/* Provide a default value for POWI_MAX_MULTS, the maximum number of
+   multiplications to inline before calling the system library's pow
+   function.  powi(x,n) requires at worst 2*bits(n)-2 multiplications,
+   so this default never requires calling pow, powf or powl.  */
+#ifndef POWI_MAX_MULTS
+#define POWI_MAX_MULTS  (2*HOST_BITS_PER_WIDE_INT-2)
+#endif
+
+/* The size of the "optimal power tree" lookup table.  All
+   exponents less than this value are simply looked up in the
+   powi_table below.  This threshold is also used to size the
+   cache of pseudo registers that hold intermediate results.  */
+#define POWI_TABLE_SIZE 256
+
+/* The size, in bits of the window, used in the "window method"
+   exponentiation algorithm.  This is equivalent to a radix of
+   (1<<POWI_WINDOW_SIZE) in the corresponding "m-ary method".  */
+#define POWI_WINDOW_SIZE 3
+
+/* The following table is an efficient representation of an
+   "optimal power tree".  For each value, i, the corresponding
+   value, j, in the table states than an optimal evaluation
+   sequence for calculating pow(x,i) can be found by evaluating
+   pow(x,j)*pow(x,i-j).  An optimal power tree for the first
+   100 integers is given in Knuth's "Seminumerical algorithms".  */
+
+static const unsigned char powi_table[POWI_TABLE_SIZE] =
+  {
+      0,   1,   1,   2,   2,   3,   3,   4,  /*   0 -   7 */
+      4,   6,   5,   6,   6,  10,   7,   9,  /*   8 -  15 */
+      8,  16,   9,  16,  10,  12,  11,  13,  /*  16 -  23 */
+     12,  17,  13,  18,  14,  24,  15,  26,  /*  24 -  31 */
+     16,  17,  17,  19,  18,  33,  19,  26,  /*  32 -  39 */
+     20,  25,  21,  40,  22,  27,  23,  44,  /*  40 -  47 */
+     24,  32,  25,  34,  26,  29,  27,  44,  /*  48 -  55 */
+     28,  31,  29,  34,  30,  60,  31,  36,  /*  56 -  63 */
+     32,  64,  33,  34,  34,  46,  35,  37,  /*  64 -  71 */
+     36,  65,  37,  50,  38,  48,  39,  69,  /*  72 -  79 */
+     40,  49,  41,  43,  42,  51,  43,  58,  /*  80 -  87 */
+     44,  64,  45,  47,  46,  59,  47,  76,  /*  88 -  95 */
+     48,  65,  49,  66,  50,  67,  51,  66,  /*  96 - 103 */
+     52,  70,  53,  74,  54, 104,  55,  74,  /* 104 - 111 */
+     56,  64,  57,  69,  58,  78,  59,  68,  /* 112 - 119 */
+     60,  61,  61,  80,  62,  75,  63,  68,  /* 120 - 127 */
+     64,  65,  65, 128,  66, 129,  67,  90,  /* 128 - 135 */
+     68,  73,  69, 131,  70,  94,  71,  88,  /* 136 - 143 */
+     72, 128,  73,  98,  74, 132,  75, 121,  /* 144 - 151 */
+     76, 102,  77, 124,  78, 132,  79, 106,  /* 152 - 159 */
+     80,  97,  81, 160,  82,  99,  83, 134,  /* 160 - 167 */
+     84,  86,  85,  95,  86, 160,  87, 100,  /* 168 - 175 */
+     88, 113,  89,  98,  90, 107,  91, 122,  /* 176 - 183 */
+     92, 111,  93, 102,  94, 126,  95, 150,  /* 184 - 191 */
+     96, 128,  97, 130,  98, 133,  99, 195,  /* 192 - 199 */
+    100, 128, 101, 123, 102, 164, 103, 138,  /* 200 - 207 */
+    104, 145, 105, 146, 106, 109, 107, 149,  /* 208 - 215 */
+    108, 200, 109, 146, 110, 170, 111, 157,  /* 216 - 223 */
+    112, 128, 113, 130, 114, 182, 115, 132,  /* 224 - 231 */
+    116, 200, 117, 132, 118, 158, 119, 206,  /* 232 - 239 */
+    120, 240, 121, 162, 122, 147, 123, 152,  /* 240 - 247 */
+    124, 166, 125, 214, 126, 138, 127, 153,  /* 248 - 255 */
+  };
+
+
+/* Return the number of multiplications required to calculate
+   powi(x,n) where n is less than POWI_TABLE_SIZE.  This is a
+   subroutine of powi_cost.  CACHE is an array indicating
+   which exponents have already been calculated.  */
+
+static int
+powi_lookup_cost (unsigned HOST_WIDE_INT n, bool *cache)
+{
+  /* If we've already calculated this exponent, then this evaluation
+     doesn't require any additional multiplications.  */
+  if (cache[n])
+    return 0;
+
+  cache[n] = true;
+  return powi_lookup_cost (n - powi_table[n], cache)
+        + powi_lookup_cost (powi_table[n], cache) + 1;
+}
+
+/* Return the number of multiplications required to calculate
+   powi(x,n) for an arbitrary x, given the exponent N.  This
+   function needs to be kept in sync with expand_powi below.  */
+
+static int
+powi_cost (HOST_WIDE_INT n)
+{
+  bool cache[POWI_TABLE_SIZE];
+  unsigned HOST_WIDE_INT digit;
+  unsigned HOST_WIDE_INT val;
+  int result;
+
+  if (n == 0)
+    return 0;
+
+  /* Ignore the reciprocal when calculating the cost.  */
+  val = (n < 0) ? -n : n;
+
+  /* Initialize the exponent cache.  */
+  memset (cache, 0, POWI_TABLE_SIZE * sizeof (bool));
+  cache[1] = true;
+
+  result = 0;
+
+  while (val >= POWI_TABLE_SIZE)
+    {
+      if (val & 1)
+       {
+         digit = val & ((1 << POWI_WINDOW_SIZE) - 1);
+         result += powi_lookup_cost (digit, cache)
+                   + POWI_WINDOW_SIZE + 1;
+         val >>= POWI_WINDOW_SIZE;
+       }
+      else
+       {
+         val >>= 1;
+         result++;
+       }
+    }
+  
+  return result + powi_lookup_cost (val, cache);
+}
+
+/* Recursive subroutine of expand_powi.  This function takes the array,
+   CACHE, of already calculated exponents and an exponent N and returns
+   an RTX that corresponds to CACHE[1]**N, as calculated in mode MODE.  */
+
+static rtx
+expand_powi_1 (enum machine_mode mode, unsigned HOST_WIDE_INT n, rtx *cache)
+{
+  unsigned HOST_WIDE_INT digit;
+  rtx target, result;
+  rtx op0, op1;
+
+  if (n < POWI_TABLE_SIZE)
+    {
+      if (cache[n])
+        return cache[n];
+
+      target = gen_reg_rtx (mode);
+      cache[n] = target;
+
+      op0 = expand_powi_1 (mode, n - powi_table[n], cache);
+      op1 = expand_powi_1 (mode, powi_table[n], cache);
+    }
+  else if (n & 1)
+    {
+      target = gen_reg_rtx (mode);
+      digit = n & ((1 << POWI_WINDOW_SIZE) - 1);
+      op0 = expand_powi_1 (mode, n - digit, cache);
+      op1 = expand_powi_1 (mode, digit, cache);
+    }
+  else
+    {
+      target = gen_reg_rtx (mode);
+      op0 = expand_powi_1 (mode, n >> 1, cache);
+      op1 = op0;
+    }
+
+  result = expand_mult (mode, op0, op1, target, 0);
+  if (result != target)
+    emit_move_insn (target, result);
+  return target;
+}
+
+/* Expand the RTL to evaluate powi(x,n) in mode MODE.  X is the
+   floating point operand in mode MODE, and N is the exponent.  This
+   function needs to be kept in sync with powi_cost above.  */
+   
+static rtx
+expand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n)
+{
+  unsigned HOST_WIDE_INT val;
+  rtx cache[POWI_TABLE_SIZE];
+  rtx result;
+
+  if (n == 0)
+    return CONST1_RTX (mode);
+
+  val = (n < 0) ? -n : n;
+
+  memset (cache, 0, sizeof (cache));
+  cache[1] = x;
+
+  result = expand_powi_1 (mode, (n < 0) ? -n : n, cache);
+
+  /* If the original exponent was negative, reciprocate the result.  */
+  if (n < 0)
+    result = expand_binop (mode, sdiv_optab, CONST1_RTX (mode),
+                          result, NULL_RTX, 0, OPTAB_LIB_WIDEN);
+
+  return result;
+}
+
+/* Expand a call to the pow built-in mathematical function.  Return 0 if
+   a normal call should be emitted rather than expanding the function
+   in-line.  EXP is the expression that is a call to the builtin
+   function; if convenient, the result should be placed in TARGET.  */
+
+static rtx
+expand_builtin_pow (tree exp, rtx target, rtx subtarget)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg0, arg1;
+
+  if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  arg0 = TREE_VALUE (arglist);
+  arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+  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);
+      n = real_to_integer (&c);
+      real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
+      if (real_identical (&c, &cint))
+       {
+         /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact.
+            Otherwise, check the number of multiplications required.
+            Note that pow never sets errno for an integer exponent.  */
+         if ((n >= -1 && n <= 2)
+             || (flag_unsafe_math_optimizations
+                 && ! optimize_size
+                 && powi_cost (n) <= POWI_MAX_MULTS))
+           {
+             enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+             rtx op = expand_expr (arg0, subtarget, VOIDmode, 0);
+             op = force_reg (mode, op);
+             return expand_powi (op, mode, n);
+           }
+       }
+    }
+  return expand_builtin_mathfn_2 (exp, target, NULL_RTX);
+}
+
 /* Expand expression EXP which is a call to the strlen builtin.  Return 0
    if we failed the caller should emit a normal call, otherwise
    try to get the result in TARGET, if convenient.  */
@@ -1957,10 +2161,22 @@ expand_builtin_strlen (tree arglist, rtx target,
       int align;
 
       /* If the length can be computed at compile-time, return it.  */
-      len = c_strlen (src);
+      len = c_strlen (src, 0);
       if (len)
        return expand_expr (len, target, target_mode, EXPAND_NORMAL);
 
+      /* If the length can be computed at compile-time and is constant
+        integer, but there are side-effects in src, evaluate
+        src for side-effects, then return len.
+        E.g. x = strlen (i++ ? "xfoo" + 1 : "bar");
+        can be optimized into: i++; x = 3;  */
+      len = c_strlen (src, 1);
+      if (len && TREE_CODE (len) == INTEGER_CST)
+       {
+         expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+       }
+
       align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
 
       /* If SRC is not a pointer type, don't do this operation inline.  */
@@ -2063,7 +2279,8 @@ expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode)
 
          /* Return an offset into the constant string argument.  */
          return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
-                                          s1, ssize_int (r - p1))),
+                                          s1, convert (TREE_TYPE (s1),
+                                                       ssize_int (r - p1)))),
                              target, mode, EXPAND_NORMAL);
        }
 
@@ -2120,7 +2337,8 @@ expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode)
 
          /* Return an offset into the constant string argument.  */
          return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
-                                          s1, ssize_int (r - p1))),
+                                          s1, convert (TREE_TYPE (s1),
+                                                       ssize_int (r - p1)))),
                              target, mode, EXPAND_NORMAL);
        }
 
@@ -2164,7 +2382,8 @@ expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode)
 
          /* Return an offset into the constant string argument.  */
          return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
-                                          s1, ssize_int (r - p1))),
+                                          s1, convert (TREE_TYPE (s1),
+                                                       ssize_int (r - p1)))),
                              target, mode, EXPAND_NORMAL);
        }
 
@@ -2210,7 +2429,8 @@ expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode)
 
          /* Return an offset into the constant string argument.  */
          return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
-                                          s1, ssize_int (r - p1))),
+                                          s1, convert (TREE_TYPE (s1),
+                                                       ssize_int (r - p1)))),
                              target, mode, EXPAND_NORMAL);
        }
 
@@ -2284,13 +2504,21 @@ expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode)
        return 0;
 
       /* If the LEN parameter is zero, return DEST.  */
-      if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+      if (integer_zerop (len))
        {
          /* Evaluate and ignore SRC in case it has side-effects.  */
          expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
          return expand_expr (dest, target, mode, EXPAND_NORMAL);
        }
 
+      /* If SRC and DEST are the same (and not volatile), return DEST.  */
+      if (operand_equal_p (src, dest, 0))
+       {
+         /* Evaluate and ignore LEN in case it has side-effects.  */
+         expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return expand_expr (dest, target, mode, EXPAND_NORMAL);
+       }
+
       /* If either SRC is not a pointer type, don't do this
          operation in-line.  */
       if (src_align == 0)
@@ -2314,10 +2542,7 @@ expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode)
                                      builtin_memcpy_read_str,
                                      (void *) src_str, dest_align, 0);
          dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_mem) != ptr_mode)
-           dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+         dest_mem = convert_memory_address (ptr_mode, dest_mem);
          return dest_mem;
        }
 
@@ -2331,10 +2556,7 @@ expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode)
       if (dest_addr == 0)
        {
          dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_addr) != ptr_mode)
-           dest_addr = convert_memory_address (ptr_mode, dest_addr);
-#endif
+         dest_addr = convert_memory_address (ptr_mode, dest_addr);
        }
       return dest_addr;
     }
@@ -2377,11 +2599,34 @@ expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode,
        = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
       rtx dest_mem, src_mem, len_rtx;
 
-      /* If DEST is not a pointer type or LEN is not constant,
-        call the normal function.  */
-      if (dest_align == 0 || !host_integerp (len, 1))
+      /* If DEST is not a pointer type, call the normal function.  */
+      if (dest_align == 0)
        return 0;
 
+      /* If SRC and DEST are the same (and not volatile), do nothing.  */
+      if (operand_equal_p (src, dest, 0))
+       {
+         tree expr;
+
+         if (endp == 0)
+           {
+             /* Evaluate and ignore LEN in case it has side-effects.  */
+             expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+             return expand_expr (dest, target, mode, EXPAND_NORMAL);
+           }
+
+         if (endp == 2)
+           len = fold (build (MINUS_EXPR, TREE_TYPE (len), dest,
+                              integer_one_node));
+         len = convert (TREE_TYPE (dest), len);
+         expr = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+         return expand_expr (expr, target, mode, EXPAND_NORMAL);
+       }
+
+      /* If LEN is not constant, call the normal function.  */
+      if (! host_integerp (len, 1))
+       return 0;
+  
       /* If the LEN parameter is zero, return DEST.  */
       if (tree_low_cst (len, 1) == 0)
        {
@@ -2413,10 +2658,7 @@ expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode,
                                      builtin_memcpy_read_str,
                                      (void *) src_str, dest_align, endp);
          dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_mem) != ptr_mode)
-           dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+         dest_mem = convert_memory_address (ptr_mode, dest_mem);
          return dest_mem;
        }
 
@@ -2431,10 +2673,7 @@ expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode,
          dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx),
                                     MIN (dest_align, src_align), endp);
          dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_mem) != ptr_mode)
-           dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+         dest_mem = convert_memory_address (ptr_mode, dest_mem);
          return dest_mem;
        }
 
@@ -2466,13 +2705,21 @@ expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode)
        return 0;
 
       /* If the LEN parameter is zero, return DEST.  */
-      if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+      if (integer_zerop (len))
        {
          /* Evaluate and ignore SRC in case it has side-effects.  */
          expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
          return expand_expr (dest, target, mode, EXPAND_NORMAL);
        }
 
+      /* If SRC and DEST are the same (and not volatile), return DEST.  */
+      if (operand_equal_p (src, dest, 0))
+       {
+         /* Evaluate and ignore LEN in case it has side-effects.  */
+         expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return expand_expr (dest, target, mode, EXPAND_NORMAL);
+       }
+
       /* If either SRC is not a pointer type, don't do this
          operation in-line.  */
       if (src_align == 0)
@@ -2535,16 +2782,21 @@ expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode)
   if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     return 0;
 
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  dst = TREE_VALUE (arglist);
+
+  /* If SRC and DST are equal (and not volatile), return DST.  */
+  if (operand_equal_p (src, dst, 0))
+    return expand_expr (dst, target, mode, EXPAND_NORMAL);
+
   fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
   if (!fn)
     return 0;
 
-  src = TREE_VALUE (TREE_CHAIN (arglist));
-  len = c_strlen (src);
-  if (len == 0)
+  len = c_strlen (src, 1);
+  if (len == 0 || TREE_SIDE_EFFECTS (len))
     return 0;
 
-  dst = TREE_VALUE (arglist);
   len = size_binop (PLUS_EXPR, len, ssize_int (1));
   arglist = build_tree_list (NULL_TREE, len);
   arglist = tree_cons (NULL_TREE, src, arglist);
@@ -2583,7 +2835,7 @@ expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode)
          because the latter will potentially produce pessimized code
          when used to produce the return value.  */
       src = TREE_VALUE (TREE_CHAIN (arglist));
-      if (! c_getstr (src) || ! (len = c_strlen (src)))
+      if (! c_getstr (src) || ! (len = c_strlen (src, 0)))
        return 0;
 
       dst = TREE_VALUE (arglist);
@@ -2622,7 +2874,7 @@ expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode)
     return 0;
   else
     {
-      tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+      tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
       tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
       tree fn;
 
@@ -2670,10 +2922,7 @@ expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode)
                           builtin_strncpy_read_str,
                           (void *) p, dest_align, 0);
          dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_mem) != ptr_mode)
-           dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+         dest_mem = convert_memory_address (ptr_mode, dest_mem);
          return dest_mem;
        }
 
@@ -2756,7 +3005,7 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
        return 0;
 
       /* If the LEN parameter is zero, return DEST.  */
-      if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+      if (integer_zerop (len))
        {
          /* Evaluate and ignore VAL in case it has side-effects.  */
          expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -2791,10 +3040,7 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
                           builtin_memset_gen_str,
                           val_rtx, dest_align, 0);
          dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_mem) != ptr_mode)
-           dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+         dest_mem = convert_memory_address (ptr_mode, dest_mem);
          return dest_mem;
        }
 
@@ -2815,10 +3061,7 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
                           builtin_memset_read_str,
                           &c, dest_align, 0);
          dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_mem) != ptr_mode)
-           dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+         dest_mem = convert_memory_address (ptr_mode, dest_mem);
          return dest_mem;
        }
 
@@ -2831,10 +3074,7 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
       if (dest_addr == 0)
        {
          dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (dest_addr) != ptr_mode)
-           dest_addr = convert_memory_address (ptr_mode, dest_addr);
-#endif
+         dest_addr = convert_memory_address (ptr_mode, dest_addr);
        }
 
       return dest_addr;
@@ -2888,7 +3128,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
   len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
 
   /* If the len parameter is zero, return zero.  */
-  if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+  if (integer_zerop (len))
     {
       /* Evaluate and ignore arg1 and arg2 in case they have
          side-effects.  */
@@ -2897,6 +3137,14 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
       return const0_rtx;
     }
 
+  /* If both arguments are equal (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    {
+      /* Evaluate and ignore len in case it has side-effects.  */
+      expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      return const0_rtx;
+    }
+
   p1 = c_getstr (arg1);
   p2 = c_getstr (arg2);
 
@@ -2913,7 +3161,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
 
   /* If len parameter is one, return an expression corresponding to
      (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1)
+  if (integer_onep (len))
     {
       tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
       tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
@@ -2929,7 +3177,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
       return expand_expr (result, target, mode, EXPAND_NORMAL);
     }
 
-#ifdef HAVE_cmpstrsi
+#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi
   {
     rtx arg1_rtx, arg2_rtx, arg3_rtx;
     rtx result;
@@ -2939,8 +3187,19 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
       = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
     int arg2_align
       = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
-    enum machine_mode insn_mode
-      = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+    enum machine_mode insn_mode;
+
+#ifdef HAVE_cmpmemsi
+    if (HAVE_cmpmemsi)
+      insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode;
+    else
+#endif
+#ifdef HAVE_cmpstrsi
+    if (HAVE_cmpstrsi)
+      insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+    else
+#endif
+      return 0;     
 
     /* If we don't have POINTER_TYPE, call the function.  */
     if (arg1_align == 0 || arg2_align == 0)
@@ -2956,11 +3215,19 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
     arg1_rtx = get_memory_rtx (arg1);
     arg2_rtx = get_memory_rtx (arg2);
     arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
-    if (!HAVE_cmpstrsi)
-      insn = NULL_RTX;
+#ifdef HAVE_cmpmemsi
+    if (HAVE_cmpmemsi)
+      insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
+                          GEN_INT (MIN (arg1_align, arg2_align)));
     else
+#endif
+#ifdef HAVE_cmpstrsi
+    if (HAVE_cmpstrsi)
       insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
                           GEN_INT (MIN (arg1_align, arg2_align)));
+    else
+#endif
+      abort ();
 
     if (insn)
       emit_insn (insn);
@@ -3007,6 +3274,10 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
   arg1 = TREE_VALUE (arglist);
   arg2 = TREE_VALUE (TREE_CHAIN (arglist));
 
+  /* If both arguments are equal (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    return const0_rtx;
+
   p1 = c_getstr (arg1);
   p2 = c_getstr (arg2);
 
@@ -3040,6 +3311,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;
 
     int arg1_align
       = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@@ -3048,8 +3320,8 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
     enum machine_mode insn_mode
       = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
 
-    len1 = c_strlen (arg1);
-    len2 = c_strlen (arg2);
+    len1 = c_strlen (arg1, 1);
+    len2 = c_strlen (arg2, 1);
 
     if (len1)
       len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
@@ -3095,24 +3367,36 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
           && REGNO (result) >= FIRST_PSEUDO_REGISTER))
       result = gen_reg_rtx (insn_mode);
 
+    /* Stabilize the arguments in case gen_cmpstrsi fails.  */
+    arg1 = save_expr (arg1);
+    arg2 = save_expr (arg2);
+
     arg1_rtx = get_memory_rtx (arg1);
     arg2_rtx = get_memory_rtx (arg2);
     arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
     insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
                         GEN_INT (MIN (arg1_align, arg2_align)));
-    if (!insn)
-      return 0;
-
-    emit_insn (insn);
+    if (insn)
+      {
+       emit_insn (insn);
+
+       /* Return the value in the proper mode for this function.  */
+       mode = TYPE_MODE (TREE_TYPE (exp));
+       if (GET_MODE (result) == mode)
+         return result;
+       if (target == 0)
+         return convert_to_mode (mode, result, 0);
+       convert_move (target, result, 0);
+       return target;
+      }
 
-    /* Return the value in the proper mode for this function.  */
-    mode = TYPE_MODE (TREE_TYPE (exp));
-    if (GET_MODE (result) == mode)
-      return result;
-    if (target == 0)
-      return convert_to_mode (mode, result, 0);
-    convert_move (target, result, 0);
-    return target;
+    /* Expand the library call ourselves using a stabilized argument
+       list to avoid re-evaluating the function's arguments twice.  */
+    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);
   }
 #endif
   return 0;
@@ -3138,7 +3422,7 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
   arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
 
   /* If the len parameter is zero, return zero.  */
-  if (host_integerp (arg3, 1) && tree_low_cst (arg3, 1) == 0)
+  if (integer_zerop (arg3))
     {
       /* Evaluate and ignore arg1 and arg2 in case they have
         side-effects.  */
@@ -3147,6 +3431,14 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
       return const0_rtx;
     }
 
+  /* If arg1 and arg2 are equal (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    {
+      /* Evaluate and ignore arg3 in case it has side-effects.  */
+      expand_expr (arg3, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      return const0_rtx;
+    }
+
   p1 = c_getstr (arg1);
   p2 = c_getstr (arg2);
 
@@ -3187,6 +3479,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;
 
     int arg1_align
       = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@@ -3195,8 +3488,8 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
     enum machine_mode insn_mode
       = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
 
-    len1 = c_strlen (arg1);
-    len2 = c_strlen (arg2);
+    len1 = c_strlen (arg1, 1);
+    len2 = c_strlen (arg2, 1);
 
     if (len1)
       len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
@@ -3245,24 +3538,38 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
           && REGNO (result) >= FIRST_PSEUDO_REGISTER))
       result = gen_reg_rtx (insn_mode);
 
+    /* Stabilize the arguments in case gen_cmpstrsi fails.  */
+    arg1 = save_expr (arg1);
+    arg2 = save_expr (arg2);
+    len = save_expr (len);
+
     arg1_rtx = get_memory_rtx (arg1);
     arg2_rtx = get_memory_rtx (arg2);
     arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
     insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
                         GEN_INT (MIN (arg1_align, arg2_align)));
-    if (!insn)
-      return 0;
-
-    emit_insn (insn);
+    if (insn)
+      {
+       emit_insn (insn);
+
+       /* Return the value in the proper mode for this function.  */
+       mode = TYPE_MODE (TREE_TYPE (exp));
+       if (GET_MODE (result) == mode)
+         return result;
+       if (target == 0)
+         return convert_to_mode (mode, result, 0);
+       convert_move (target, result, 0);
+       return target;
+      }
 
-    /* Return the value in the proper mode for this function.  */
-    mode = TYPE_MODE (TREE_TYPE (exp));
-    if (GET_MODE (result) == mode)
-      return result;
-    if (target == 0)
-      return convert_to_mode (mode, result, 0);
-    convert_move (target, result, 0);
-    return target;
+    /* Expand the library call ourselves using a stabilized argument
+       list to avoid re-evaluating the function's arguments twice.  */
+    arglist = build_tree_list (NULL_TREE, len);
+    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);
   }
 #endif
   return 0;
@@ -3283,9 +3590,52 @@ expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode)
        src = TREE_VALUE (TREE_CHAIN (arglist));
       const char *p = c_getstr (src);
 
-      /* If the string length is zero, return the dst parameter.  */
-      if (p && *p == '\0')
-       return expand_expr (dst, target, mode, EXPAND_NORMAL);
+      if (p)
+       {
+         /* If the string length is zero, return the dst parameter.  */
+         if (*p == '\0')
+           return expand_expr (dst, target, mode, EXPAND_NORMAL);
+         else if (!optimize_size)
+           {
+             /* Otherwise if !optimize_size, see if we can store by
+                 pieces into (dst + strlen(dst)).  */
+             tree newdst, arglist,
+               strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
+             
+             /* This is the length argument.  */
+             arglist = build_tree_list (NULL_TREE,
+                                        fold (size_binop (PLUS_EXPR,
+                                                          c_strlen (src, 0),
+                                                          ssize_int (1))));
+             /* Prepend src argument.  */
+             arglist = tree_cons (NULL_TREE, src, arglist);
+             
+             /* We're going to use dst more than once.  */
+             dst = save_expr (dst);
+
+             /* Create strlen (dst).  */
+             newdst =
+               fold (build_function_call_expr (strlen_fn,
+                                               build_tree_list (NULL_TREE,
+                                                                dst)));
+             /* Create (dst + strlen (dst)).  */
+             newdst = fold (build (PLUS_EXPR, TREE_TYPE (dst), dst, newdst));
+
+             /* Prepend the new dst argument.  */
+             arglist = tree_cons (NULL_TREE, newdst, arglist);
+             
+             /* We don't want to get turned into a memcpy if the
+                 target is const0_rtx, i.e. when the return value
+                 isn't used.  That would produce pessimized code so
+                 pass in a target of zero, it should never actually be
+                 used.  If this was successful return the original
+                 dst, not the result of mempcpy.  */
+             if (expand_builtin_mempcpy (arglist, /*target=*/0, mode, /*endp=*/0))
+               return expand_expr (dst, target, mode, EXPAND_NORMAL);
+             else
+               return 0;
+           }
+       }
 
       return 0;
     }
@@ -3441,21 +3791,8 @@ expand_builtin_saveregs (void)
 
   start_sequence ();
 
-#ifdef EXPAND_BUILTIN_SAVEREGS
   /* Do whatever the machine needs done in this case.  */
-  val = EXPAND_BUILTIN_SAVEREGS ();
-#else
-  /* ??? We used to try and build up a call to the out of line function,
-     guessing about what registers needed saving etc.  This became much
-     harder with __builtin_va_start, since we don't have a tree for a
-     call to __builtin_saveregs to fall back on.  There was exactly one
-     port (i860) that used this code, and I'm unconvinced it could actually
-     handle the general case.  So we no longer try to handle anything
-     weird and make the backend absorb the evil.  */
-
-  error ("__builtin_saveregs not supported by this target");
-  val = const0_rtx;
-#endif
+  val = targetm.calls.expand_builtin_saveregs ();
 
   seq = get_insns ();
   end_sequence ();
@@ -3595,6 +3932,14 @@ stabilize_va_list (tree valist, int needs_lvalue)
   return valist;
 }
 
+/* The "standard" definition of va_list is void*.  */
+
+tree
+std_build_builtin_va_list (void)
+{
+  return ptr_type_node;
+}
+
 /* The "standard" implementation of va_start: just assign `nextarg' to
    the variable.  */
 
@@ -3783,10 +4128,7 @@ expand_builtin_va_arg (tree valist, tree type)
 #endif
     }
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (addr) != Pmode)
-    addr = convert_memory_address (Pmode, addr);
-#endif
+  addr = convert_memory_address (Pmode, addr);
 
   result = gen_rtx_MEM (TYPE_MODE (type), addr);
   set_mem_alias_set (result, get_varargs_alias_set ());
@@ -3845,13 +4187,8 @@ expand_builtin_va_copy (tree arglist)
       size = expand_expr (TYPE_SIZE_UNIT (va_list_type_node), NULL_RTX,
                          VOIDmode, EXPAND_NORMAL);
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-      if (GET_MODE (dstb) != Pmode)
-       dstb = convert_memory_address (Pmode, dstb);
-
-      if (GET_MODE (srcb) != Pmode)
-       srcb = convert_memory_address (Pmode, srcb);
-#endif
+      dstb = convert_memory_address (Pmode, dstb);
+      srcb = convert_memory_address (Pmode, srcb);
 
       /* "Dereference" to BLKmode memories.  */
       dstb = gen_rtx_MEM (BLKmode, dstb);
@@ -3934,11 +4271,7 @@ expand_builtin_alloca (tree arglist, rtx target)
 
   /* Allocate the desired space.  */
   result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
-
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (result) != ptr_mode)
-    result = convert_memory_address (ptr_mode, result);
-#endif
+  result = convert_memory_address (ptr_mode, result);
 
   return result;
 }
@@ -3972,7 +4305,7 @@ expand_builtin_unop (enum machine_mode target_mode, tree arglist, rtx target,
    long, we attempt to transform this call into __builtin_fputc().  */
 
 static rtx
-expand_builtin_fputs (tree arglist, int ignore, int unlocked)
+expand_builtin_fputs (tree arglist, rtx target, bool unlocked)
 {
   tree len, fn;
   tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
@@ -3982,7 +4315,7 @@ expand_builtin_fputs (tree arglist, int ignore, int unlocked)
 
   /* If the return value is used, or the replacement _DECL isn't
      initialized, don't do the transformation.  */
-  if (!ignore || !fn_fputc || !fn_fwrite)
+  if (target != const0_rtx || !fn_fputc || !fn_fwrite)
     return 0;
 
   /* Verify the arguments in the original call.  */
@@ -3991,7 +4324,7 @@ expand_builtin_fputs (tree arglist, int ignore, int unlocked)
 
   /* Get the length of the string passed to fputs.  If the length
      can't be determined, punt.  */
-  if (!(len = c_strlen (TREE_VALUE (arglist)))
+  if (!(len = c_strlen (TREE_VALUE (arglist), 1))
       || TREE_CODE (len) != INTEGER_CST)
     return 0;
 
@@ -4021,7 +4354,7 @@ expand_builtin_fputs (tree arglist, int ignore, int unlocked)
            break;
          }
       }
-      /* FALLTHROUGH */
+      /* Fall through.  */
     case 1: /* length is greater than 1, call fwrite.  */
       {
        tree string_arg;
@@ -4044,8 +4377,7 @@ expand_builtin_fputs (tree arglist, int ignore, int unlocked)
     }
 
   return expand_expr (build_function_call_expr (fn, arglist),
-                     (ignore ? const0_rtx : NULL_RTX),
-                     VOIDmode, EXPAND_NORMAL);
+                     const0_rtx, VOIDmode, EXPAND_NORMAL);
 }
 
 /* Expand a call to __builtin_expect.  We return our argument and emit a
@@ -4082,7 +4414,7 @@ expand_builtin_expect (tree arglist, rtx target)
 
       rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL);
 
-      note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE);
+      note = emit_note (NOTE_INSN_EXPECTED_VALUE);
       NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c);
     }
 
@@ -4110,6 +4442,7 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label)
       && (integer_zerop (arg1) || integer_onep (arg1)))
     {
       int num_jumps = 0;
+      int save_pending_stack_adjust = pending_stack_adjust;
       rtx insn;
 
       /* If we fail to locate an appropriate conditional jump, we'll
@@ -4134,6 +4467,14 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label)
       ret = get_insns ();
       end_sequence ();
 
+      /* For mildly unsafe builtin jump's, if unsave_expr_now
+        creates a new tree instead of changing the old one
+        TREE_VALUE (arglist) needs to be updated.  */
+      if (arg0 != TREE_VALUE (arglist)
+         && TREE_CODE (arg0) == UNSAVE_EXPR
+         && TREE_OPERAND (arg0, 0) != TREE_VALUE (arglist))
+       TREE_VALUE (arglist) = TREE_OPERAND (arg0, 0);
+
       /* Now that the __builtin_expect has been validated, go through and add
         the expect's to each of the conditional jumps.  If we run into an
         error, just give up and generate the 'safe' code of doing a SCC
@@ -4201,7 +4542,10 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label)
       /* If no jumps were modified, fail and do __builtin_expect the normal
         way.  */
       if (num_jumps == 0)
-       ret = NULL_RTX;
+       {
+         ret = NULL_RTX;
+         pending_stack_adjust = save_pending_stack_adjust;
+       }
     }
 
   return ret;
@@ -4265,6 +4609,227 @@ expand_builtin_cabs (tree arglist, rtx target)
   return expand_complex_abs (mode, op0, target, 0);
 }
 
+/* Create a new constant string literal and return a char* pointer to it.
+   The STRING_CST value is the LEN characters at STR.  */
+static tree
+build_string_literal (int len, const char *str)
+{
+  tree t, elem, index, type;
+
+  t = build_string (len, str);
+  elem = build_type_variant (char_type_node, 1, 0);
+  index = build_index_type (build_int_2 (len - 1, 0));
+  type = build_array_type (elem, index);
+  TREE_TYPE (t) = type;
+  TREE_CONSTANT (t) = 1;
+  TREE_READONLY (t) = 1;
+  TREE_STATIC (t) = 1;
+
+  type = build_pointer_type (type);
+  t = build1 (ADDR_EXPR, type, t);
+
+  type = build_pointer_type (elem);
+  t = build1 (NOP_EXPR, type, t);
+  return t;
+}
+
+/* Expand a call to printf or printf_unlocked with argument list ARGLIST.
+   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,
+                      bool unlocked)
+{
+  tree fn_putchar = unlocked
+                   ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
+                   : implicit_built_in_decls[BUILT_IN_PUTCHAR];
+  tree fn_puts = unlocked ? implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED]
+                         : implicit_built_in_decls[BUILT_IN_PUTS];
+  const char *fmt_str;
+  tree fn, fmt, arg;
+
+  /* If the return value is used, don't do the transformation.  */
+  if (target != const0_rtx)
+    return 0;
+
+  /* Verify the required arguments in the original call.  */
+  if (! arglist)
+    return 0;
+  fmt = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return 0;
+
+  /* If the format specifier was "%s\n", call __builtin_puts(arg).  */
+  if (strcmp (fmt_str, "%s\n") == 0)
+    {
+      if (! arglist
+          || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+         || TREE_CHAIN (arglist))
+       return 0;
+      fn = fn_puts;
+    }
+  /* If the format specifier was "%c", call __builtin_putchar(arg).  */
+  else if (strcmp (fmt_str, "%c") == 0)
+    {
+      if (! arglist
+         || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+         || TREE_CHAIN (arglist))
+       return 0;
+      fn = fn_putchar;
+    }
+  else
+    {
+      /* We can't handle anything else with % args or %% ... yet.  */
+      if (strchr (fmt_str, '%'))
+        return 0;
+
+      if (arglist)
+       return 0;
+
+      /* If the format specifier was "", printf does nothing.  */
+      if (fmt_str[0] == '\0')
+       return const0_rtx;
+      /* If the format specifier has length of 1, call putchar.  */
+      if (fmt_str[1] == '\0')
+       {
+         /* Given printf("c"), (where c is any one character,)
+            convert "c"[0] to an int and pass that to the replacement
+            function.  */
+         arg = build_int_2 (fmt_str[0], 0);
+         arglist = build_tree_list (NULL_TREE, arg);
+         fn = fn_putchar;
+       }
+      else
+       {
+         /* If the format specifier was "string\n", call puts("string").  */
+         size_t len = strlen (fmt_str);
+         if (fmt_str[len - 1] == '\n')
+           {
+             /* Create a NUL-terminated string that's one char shorter
+                than the original, stripping off the trailing '\n'.  */
+             char *newstr = (char *) alloca (len);
+             memcpy (newstr, fmt_str, len - 1);
+             newstr[len - 1] = 0;
+
+             arg = build_string_literal (len, newstr);
+             arglist = build_tree_list (NULL_TREE, arg);
+             fn = fn_puts;
+           }
+         else
+           /* We'd like to arrange to call fputs(string,stdout) here,
+              but we need stdout and don't have a way to get it yet.  */
+           return 0;
+       }
+    }
+
+  if (!fn)
+    return 0;
+  return expand_expr (build_function_call_expr (fn, arglist),
+                     target, mode, EXPAND_NORMAL);
+}
+
+/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST.
+   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,
+                       bool unlocked)
+{
+  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]
+                          : implicit_built_in_decls[BUILT_IN_FPUTS];
+  const char *fmt_str;
+  tree fn, fmt, fp, arg;
+
+  /* If the return value is used, don't do the transformation.  */
+  if (target != const0_rtx)
+    return 0;
+
+  /* Verify the required arguments in the original call.  */
+  if (! arglist)
+    return 0;
+  fp = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  fmt = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return 0;
+
+  /* If the format specifier was "%s", call __builtin_fputs(arg,fp).  */
+  if (strcmp (fmt_str, "%s") == 0)
+    {
+      if (! arglist
+          || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+         || TREE_CHAIN (arglist))
+       return 0;
+      arg = TREE_VALUE (arglist);
+      arglist = build_tree_list (NULL_TREE, fp);
+      arglist = tree_cons (NULL_TREE, arg, arglist);
+      fn = fn_fputs;
+    }
+  /* If the format specifier was "%c", call __builtin_fputc(arg,fp).  */
+  else if (strcmp (fmt_str, "%c") == 0)
+    {
+      if (! arglist
+         || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+         || TREE_CHAIN (arglist))
+       return 0;
+      arg = TREE_VALUE (arglist);
+      arglist = build_tree_list (NULL_TREE, fp);
+      arglist = tree_cons (NULL_TREE, arg, arglist);
+      fn = fn_fputc;
+    }
+  else
+    {
+      /* We can't handle anything else with % args or %% ... yet.  */
+      if (strchr (fmt_str, '%'))
+        return 0;
+
+      if (arglist)
+       return 0;
+
+      /* If the format specifier was "", fprintf does nothing.  */
+      if (fmt_str[0] == '\0')
+       {
+         /* Evaluate and ignore FILE* argument for side-effects.  */
+         expand_expr (fp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return const0_rtx;
+       }
+
+      /* When "string" doesn't contain %, replace all cases of
+        fprintf(stream,string) with fputs(string,stream).  The fputs
+        builtin will take care of special cases like length == 1.  */
+      arglist = build_tree_list (NULL_TREE, fp);
+      arglist = tree_cons (NULL_TREE, fmt, arglist);
+      fn = fn_fputs;
+    }
+
+  if (!fn)
+    return 0;
+  return expand_expr (build_function_call_expr (fn, arglist),
+                     target, mode, EXPAND_NORMAL);
+}
+
 /* Expand a call to sprintf with argument list ARGLIST.  Return 0 if
    a normal call should be emitted rather than expanding the function
    inline.  If convenient, the result should be placed in TARGET with
@@ -4273,8 +4838,8 @@ expand_builtin_cabs (tree arglist, rtx target)
 static rtx
 expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
 {
-  tree dest, fmt, stripped;
-  tree orig_arglist;
+  tree orig_arglist, dest, fmt;
+  const char *fmt_str;
 
   orig_arglist = arglist;
 
@@ -4288,41 +4853,38 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
   if (! arglist)
     return 0;
   fmt = TREE_VALUE (arglist);
-  if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+  if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
     return 0;
   arglist = TREE_CHAIN (arglist);
 
   /* Check whether the format is a literal string constant.  */
-  stripped = fmt;
-  STRIP_NOPS (stripped);
-  if (stripped && TREE_CODE (stripped) == ADDR_EXPR)
-    stripped = TREE_OPERAND (stripped, 0);
-  if (TREE_CODE (stripped) != STRING_CST)
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
     return 0;
 
   /* If the format doesn't contain % args or %%, use strcpy.  */
-  if (strchr (TREE_STRING_POINTER (stripped), '%') == 0)
+  if (strchr (fmt_str, '%') == 0)
     {
       tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
       tree exp;
 
-      if (arglist || !fn)
+      if (arglist || ! fn)
        return 0;
       expand_expr (build_function_call_expr (fn, orig_arglist),
                   const0_rtx, VOIDmode, EXPAND_NORMAL);
       if (target == const0_rtx)
        return const0_rtx;
-      exp = build_int_2 (TREE_STRING_LENGTH (stripped) - 1, 0);
+      exp = build_int_2 (strlen (fmt_str), 0);
       exp = fold (build1 (NOP_EXPR, integer_type_node, exp));
       return expand_expr (exp, target, mode, EXPAND_NORMAL);
     }
-  /* If the format is "%s", use strcpy and possibly strlen.  */
-  else if (strcmp (TREE_STRING_POINTER (stripped), "%s") == 0)
+  /* If the format is "%s", use strcpy if the result isn't used.  */
+  else if (strcmp (fmt_str, "%s") == 0)
     {
-      tree strcpy_fn, strlen_fn, exp, arg;
-      strcpy_fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+      tree fn, arg, len;
+      fn = implicit_built_in_decls[BUILT_IN_STRCPY];
 
-      if (! strcpy_fn)
+      if (! fn)
        return 0;
 
       if (! arglist || TREE_CHAIN (arglist))
@@ -4333,25 +4895,21 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
 
       if (target != const0_rtx)
        {
-         strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
-         if (! strlen_fn)
+         len = c_strlen (arg, 1);
+         if (! len || TREE_CODE (len) != INTEGER_CST)
            return 0;
-         arg = save_expr (arg);
        }
       else
-       strlen_fn = 0;
+       len = NULL_TREE;
 
       arglist = build_tree_list (NULL_TREE, arg);
       arglist = tree_cons (NULL_TREE, dest, arglist);
-      expand_expr (build_function_call_expr (strcpy_fn, arglist),
+      expand_expr (build_function_call_expr (fn, arglist),
                   const0_rtx, VOIDmode, EXPAND_NORMAL);
 
       if (target == const0_rtx)
        return const0_rtx;
-
-      exp = build_function_call_expr (strlen_fn, TREE_CHAIN (arglist));
-      exp = fold (build1 (NOP_EXPR, integer_type_node, exp));
-      return expand_expr (exp, target, mode, EXPAND_NORMAL);
+      return expand_expr (len, target, mode, EXPAND_NORMAL);
     }
 
   return 0;
@@ -4367,12 +4925,12 @@ rtx
 expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
                int ignore)
 {
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
   enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
 
-  /* Perform postincrements before expanding builtin functions. Â */
+  /* Perform postincrements before expanding builtin functions.  */
   emit_queue ();
 
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
@@ -4380,94 +4938,11 @@ 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))
-    switch (fcode)
-      {
-      case BUILT_IN_SQRT:
-      case BUILT_IN_SQRTF:
-      case BUILT_IN_SQRTL:
-      case BUILT_IN_SIN:
-      case BUILT_IN_SINF:
-      case BUILT_IN_SINL:
-      case BUILT_IN_COS:
-      case BUILT_IN_COSF:
-      case BUILT_IN_COSL:
-      case BUILT_IN_EXP:
-      case BUILT_IN_EXPF:
-      case BUILT_IN_EXPL:
-      case BUILT_IN_LOG:
-      case BUILT_IN_LOGF:
-      case BUILT_IN_LOGL:
-      case BUILT_IN_TAN:
-      case BUILT_IN_TANF:
-      case BUILT_IN_TANL:
-      case BUILT_IN_ATAN:
-      case BUILT_IN_ATANF:
-      case BUILT_IN_ATANL:
-      case BUILT_IN_POW:
-      case BUILT_IN_POWF:
-      case BUILT_IN_POWL:
-      case BUILT_IN_ATAN2:
-      case BUILT_IN_ATAN2F:
-      case BUILT_IN_ATAN2L:
-      case BUILT_IN_MEMSET:
-      case BUILT_IN_MEMCPY:
-      case BUILT_IN_MEMCMP:
-      case BUILT_IN_MEMPCPY:
-      case BUILT_IN_MEMMOVE:
-      case BUILT_IN_BCMP:
-      case BUILT_IN_BZERO:
-      case BUILT_IN_BCOPY:
-      case BUILT_IN_INDEX:
-      case BUILT_IN_RINDEX:
-      case BUILT_IN_SPRINTF:
-      case BUILT_IN_STPCPY:
-      case BUILT_IN_STRCHR:
-      case BUILT_IN_STRRCHR:
-      case BUILT_IN_STRLEN:
-      case BUILT_IN_STRCPY:
-      case BUILT_IN_STRNCPY:
-      case BUILT_IN_STRNCMP:
-      case BUILT_IN_STRSTR:
-      case BUILT_IN_STRPBRK:
-      case BUILT_IN_STRCAT:
-      case BUILT_IN_STRNCAT:
-      case BUILT_IN_STRSPN:
-      case BUILT_IN_STRCSPN:
-      case BUILT_IN_STRCMP:
-      case BUILT_IN_FFS:
-      case BUILT_IN_PUTCHAR:
-      case BUILT_IN_PUTS:
-      case BUILT_IN_PRINTF:
-      case BUILT_IN_FPUTC:
-      case BUILT_IN_FPUTS:
-      case BUILT_IN_FWRITE:
-      case BUILT_IN_PUTCHAR_UNLOCKED:
-      case BUILT_IN_PUTS_UNLOCKED:
-      case BUILT_IN_PRINTF_UNLOCKED:
-      case BUILT_IN_FPUTC_UNLOCKED:
-      case BUILT_IN_FPUTS_UNLOCKED:
-      case BUILT_IN_FWRITE_UNLOCKED:
-      case BUILT_IN_FLOOR:
-      case BUILT_IN_FLOORF:
-      case BUILT_IN_FLOORL:
-      case BUILT_IN_CEIL:
-      case BUILT_IN_CEILF:
-      case BUILT_IN_CEILL:
-      case BUILT_IN_TRUNC:
-      case BUILT_IN_TRUNCF:
-      case BUILT_IN_TRUNCL:
-      case BUILT_IN_ROUND:
-      case BUILT_IN_ROUNDF:
-      case BUILT_IN_ROUNDL:
-      case BUILT_IN_NEARBYINT:
-      case BUILT_IN_NEARBYINTF:
-      case BUILT_IN_NEARBYINTL:
-       return expand_call (exp, target, ignore);
-
-      default:
-       break;
-      }
+  if (!optimize
+      && !CALLED_AS_BUILT_IN (fndecl)
+      && DECL_ASSEMBLER_NAME_SET_P (fndecl)
+      && fcode != BUILT_IN_ALLOCA)
+    return expand_call (exp, target, ignore);
 
   /* The built-in function expanders test for target == const0_rtx
      to determine whether the function's result will be ignored.  */
@@ -4588,6 +5063,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
     case BUILT_IN_POW:
     case BUILT_IN_POWF:
     case BUILT_IN_POWL:
+      if (! flag_unsafe_math_optimizations)
+       break;
+      target = expand_builtin_pow (exp, target, subtarget);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_ATAN2:
     case BUILT_IN_ATAN2F:
     case BUILT_IN_ATAN2L:
@@ -4881,13 +5363,38 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       expand_builtin_trap ();
       return const0_rtx;
 
+    case BUILT_IN_PRINTF:
+      target = expand_builtin_printf (arglist, target, mode, false);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_PRINTF_UNLOCKED:
+      target = expand_builtin_printf (arglist, target, mode, true);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_FPUTS:
-      target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 0);
+      target = expand_builtin_fputs (arglist, target, false);
       if (target)
        return target;
       break;
+
     case BUILT_IN_FPUTS_UNLOCKED:
-      target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 1);
+      target = expand_builtin_fputs (arglist, target, true);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FPRINTF:
+      target = expand_builtin_fprintf (arglist, target, mode, false);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FPRINTF_UNLOCKED:
+      target = expand_builtin_fprintf (arglist, target, mode, true);
       if (target)
        return target;
       break;
@@ -4949,52 +5456,74 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
 }
 
 /* Determine whether a tree node represents a call to a built-in
-   math function.  If the tree T is a call to a built-in function
-   taking a single real argument, then the return value is the
-   DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.  Otherwise
-   the return value is END_BUILTINS.  */
+   function.  If the tree T is a call to a built-in function with
+   the right number of arguments of the appropriate types, return
+   the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.
+   Otherwise the return value is END_BUILTINS.  */
 
 enum built_in_function
 builtin_mathfn_code (tree t)
 {
-  tree fndecl, arglist;
+  tree fndecl, arglist, parmlist;
+  tree argtype, parmtype;
 
   if (TREE_CODE (t) != CALL_EXPR
       || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR)
     return END_BUILTINS;
 
-  fndecl = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-  if (TREE_CODE (fndecl) != FUNCTION_DECL
+  fndecl = get_callee_fndecl (t);
+  if (fndecl == NULL_TREE
+      || TREE_CODE (fndecl) != FUNCTION_DECL
       || ! DECL_BUILT_IN (fndecl)
       || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
     return END_BUILTINS;
 
   arglist = TREE_OPERAND (t, 1);
-  if (! arglist
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE)
-    return END_BUILTINS;
-
-  arglist = TREE_CHAIN (arglist);
-  switch (DECL_FUNCTION_CODE (fndecl))
+  parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  for (; parmlist; parmlist = TREE_CHAIN (parmlist))
     {
-    case BUILT_IN_POW:
-    case BUILT_IN_POWF:
-    case BUILT_IN_POWL:
-    case BUILT_IN_ATAN2:
-    case BUILT_IN_ATAN2F:
-    case BUILT_IN_ATAN2L:
-      if (! arglist
-         || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE
-         || TREE_CHAIN (arglist))
+      /* If a function doesn't take a variable number of arguments,
+        the last element in the list will have type `void'.  */
+      parmtype = TREE_VALUE (parmlist);
+      if (VOID_TYPE_P (parmtype))
+       {
+         if (arglist)
+           return END_BUILTINS;
+         return DECL_FUNCTION_CODE (fndecl);
+       }
+
+      if (! arglist)
        return END_BUILTINS;
-      break;
 
-    default:
-      if (arglist)
+      argtype = TREE_TYPE (TREE_VALUE (arglist));
+
+      if (SCALAR_FLOAT_TYPE_P (parmtype))
+       {
+         if (! SCALAR_FLOAT_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else if (COMPLEX_FLOAT_TYPE_P (parmtype))
+       {
+         if (! COMPLEX_FLOAT_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else if (POINTER_TYPE_P (parmtype))
+       {
+         if (! POINTER_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else if (INTEGRAL_TYPE_P (parmtype))
+       {
+         if (! INTEGRAL_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else
        return END_BUILTINS;
-      break;
+
+      arglist = TREE_CHAIN (arglist);
     }
 
+  /* Variable-length argument list.  */
   return DECL_FUNCTION_CODE (fndecl);
 }
 
@@ -5022,15 +5551,14 @@ fold_builtin_constant_p (tree arglist)
          && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST))
     return integer_one_node;
 
-  /* If we aren't going to be running CSE or 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 those
-     case we only want literals, since those are only optimized
+  /* 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
+     those case we only want literals, since those are only optimized
      when generating RTL, not later.
      And finally, if we are compiling an initializer, not code, we
      need to return a definite result now; there's not going to be any
      more optimization done.  */
-  if (TREE_SIDE_EFFECTS (arglist) || cse_not_expected
+  if (TREE_SIDE_EFFECTS (arglist)
       || AGGREGATE_TYPE_P (TREE_TYPE (arglist))
       || POINTER_TYPE_P (TREE_TYPE (arglist))
       || cfun == 0)
@@ -5084,24 +5612,123 @@ fold_builtin_nan (tree arglist, tree type, int quiet)
   return build_real (type, real);
 }
 
-/* EXP is assumed to me builtin call where truncation can be propagated
-   across (for instance floor((double)f) == (double)floorf (f).
-   Do the transformation.  */
-static tree
-fold_trunc_transparent_mathfn (tree exp)
-{
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
-  tree arglist = TREE_OPERAND (exp, 1);
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+/* Return true if the floating point expression T has an integer value.
+   We also allow +Inf, -Inf and NaN to be considered integer values.  */
 
-  if (optimize && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+static bool
+integer_valued_real_p (tree t)
+{
+  switch (TREE_CODE (t))
     {
-      tree arg0 = strip_float_extensions (TREE_VALUE (arglist));
-      tree ftype = TREE_TYPE (exp);
-      tree newtype = TREE_TYPE (arg0);
-      tree decl;
+    case FLOAT_EXPR:
+      return true;
+
+    case ABS_EXPR:
+    case SAVE_EXPR:
+    case NON_LVALUE_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 0));
+
+    case COMPOUND_EXPR:
+    case MODIFY_EXPR:
+    case BIND_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 1));
+
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 0))
+            && integer_valued_real_p (TREE_OPERAND (t, 1));
+
+    case COND_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 1))
+            && integer_valued_real_p (TREE_OPERAND (t, 2));
+
+    case REAL_CST:
+      if (! TREE_CONSTANT_OVERFLOW (t))
+      {
+        REAL_VALUE_TYPE c, cint;
 
-      if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
+       c = TREE_REAL_CST (t);
+       real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c);
+       return real_identical (&c, &cint);
+      }
+
+    case NOP_EXPR:
+      {
+       tree type = TREE_TYPE (TREE_OPERAND (t, 0));
+       if (TREE_CODE (type) == INTEGER_TYPE)
+         return true;
+       if (TREE_CODE (type) == REAL_TYPE)
+         return integer_valued_real_p (TREE_OPERAND (t, 0));
+       break;
+      }
+
+    case CALL_EXPR:
+      switch (builtin_mathfn_code (t))
+       {
+       case BUILT_IN_CEIL:
+       case BUILT_IN_CEILF:
+       case BUILT_IN_CEILL:
+       case BUILT_IN_FLOOR:
+       case BUILT_IN_FLOORF:
+       case BUILT_IN_FLOORL:
+       case BUILT_IN_NEARBYINT:
+       case BUILT_IN_NEARBYINTF:
+       case BUILT_IN_NEARBYINTL:
+       case BUILT_IN_ROUND:
+       case BUILT_IN_ROUNDF:
+       case BUILT_IN_ROUNDL:
+       case BUILT_IN_TRUNC:
+       case BUILT_IN_TRUNCF:
+       case BUILT_IN_TRUNCL:
+         return true;
+
+       default:
+         break;
+       }
+      break;
+
+    default:
+      break;
+    }
+  return false;
+}
+
+/* EXP is assumed to be builtin call where truncation can be propagated
+   across (for instance floor((double)f) == (double)floorf (f).
+   Do the transformation.  */
+
+static tree
+fold_trunc_transparent_mathfn (tree exp)
+{
+  tree fndecl = get_callee_fndecl (exp);
+  tree arglist = TREE_OPERAND (exp, 1);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  arg = TREE_VALUE (arglist);
+  /* Integer rounding functions are idempotent.  */
+  if (fcode == builtin_mathfn_code (arg))
+    return arg;
+
+  /* If argument is already integer valued, and we don't need to worry
+     about setting errno, there's no need to perform rounding.  */
+  if (! flag_errno_math && integer_valued_real_p (arg))
+    return arg;
+
+  if (optimize)
+    {
+      tree arg0 = strip_float_extensions (arg);
+      tree ftype = TREE_TYPE (exp);
+      tree newtype = TREE_TYPE (arg0);
+      tree decl;
+
+      if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
          && (decl = mathfn_built_in (newtype, fcode)))
        {
          arglist =
@@ -5178,6 +5805,8 @@ fold_builtin_cabs (tree fndecl, tree arglist, tree type)
        {
          tree rpart, ipart, result, arglist;
 
+         arg = save_expr (arg);
+
          rpart = fold (build1 (REALPART_EXPR, type, arg));
          ipart = fold (build1 (IMAGPART_EXPR, type, arg));
 
@@ -5198,13 +5827,676 @@ fold_builtin_cabs (tree fndecl, tree arglist, tree type)
   return NULL_TREE;
 }
 
+/* Fold function call to builtin trunc, truncf or truncl.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_trunc (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  /* Optimize trunc of constant value.  */
+  arg = TREE_VALUE (arglist);
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+    {
+      REAL_VALUE_TYPE r, x;
+      tree type = TREE_TYPE (exp);
+
+      x = TREE_REAL_CST (arg);
+      real_trunc (&r, TYPE_MODE (type), &x);
+      return build_real (type, r);
+    }
+
+  return fold_trunc_transparent_mathfn (exp);
+}
+
+/* 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)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  /* Optimize floor of constant value.  */
+  arg = TREE_VALUE (arglist);
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+    {
+      REAL_VALUE_TYPE x;
+
+      x = TREE_REAL_CST (arg);
+      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+       {
+         tree type = TREE_TYPE (exp);
+         REAL_VALUE_TYPE r;
+
+         real_floor (&r, TYPE_MODE (type), &x);
+         return build_real (type, r);
+       }
+    }
+
+  return fold_trunc_transparent_mathfn (exp);
+}
+
+/* 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)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  /* Optimize ceil of constant value.  */
+  arg = TREE_VALUE (arglist);
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+    {
+      REAL_VALUE_TYPE x;
+
+      x = TREE_REAL_CST (arg);
+      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+       {
+         tree type = TREE_TYPE (exp);
+         REAL_VALUE_TYPE r;
+
+         real_ceil (&r, TYPE_MODE (type), &x);
+         return build_real (type, r);
+       }
+    }
+
+  return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin ffs, clz, ctz, popcount and parity
+   and their long and long long variants (i.e. ffsl and ffsll).
+   Return NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_bitop (tree exp)
+{
+  tree fndecl = get_callee_fndecl (exp);
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg;
+
+  if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
+    return NULL_TREE;
+
+  /* Optimize for constant argument.  */
+  arg = TREE_VALUE (arglist);
+  if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+    {
+      HOST_WIDE_INT hi, width, result;
+      unsigned HOST_WIDE_INT lo;
+      tree type, t;
+
+      type = TREE_TYPE (arg);
+      width = TYPE_PRECISION (type);
+      lo = TREE_INT_CST_LOW (arg);
+
+      /* Clear all the bits that are beyond the type's precision.  */
+      if (width > HOST_BITS_PER_WIDE_INT)
+       {
+         hi = TREE_INT_CST_HIGH (arg);
+         if (width < 2 * HOST_BITS_PER_WIDE_INT)
+           hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT));
+       }
+      else
+       {
+         hi = 0;
+         if (width < HOST_BITS_PER_WIDE_INT)
+           lo &= ~((unsigned HOST_WIDE_INT) (-1) << width);
+       }
+
+      switch (DECL_FUNCTION_CODE (fndecl))
+       {
+       case BUILT_IN_FFS:
+       case BUILT_IN_FFSL:
+       case BUILT_IN_FFSLL:
+         if (lo != 0)
+           result = exact_log2 (lo & -lo) + 1;
+         else if (hi != 0)
+           result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi) + 1;
+         else
+           result = 0;
+         break;
+
+       case BUILT_IN_CLZ:
+       case BUILT_IN_CLZL:
+       case BUILT_IN_CLZLL:
+         if (hi != 0)
+           result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT;
+         else if (lo != 0)
+           result = width - floor_log2 (lo) - 1;
+         else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
+           result = width;
+         break;
+
+       case BUILT_IN_CTZ:
+       case BUILT_IN_CTZL:
+       case BUILT_IN_CTZLL:
+         if (lo != 0)
+           result = exact_log2 (lo & -lo);
+         else if (hi != 0)
+           result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi);
+         else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
+           result = width;
+         break;
+
+       case BUILT_IN_POPCOUNT:
+       case BUILT_IN_POPCOUNTL:
+       case BUILT_IN_POPCOUNTLL:
+         result = 0;
+         while (lo)
+           result++, lo &= lo - 1;
+         while (hi)
+           result++, hi &= hi - 1;
+         break;
+
+       case BUILT_IN_PARITY:
+       case BUILT_IN_PARITYL:
+       case BUILT_IN_PARITYLL:
+         result = 0;
+         while (lo)
+           result++, lo &= lo - 1;
+         while (hi)
+           result++, hi &= hi - 1;
+         result &= 1;
+         break;
+
+       default:
+         abort();
+       }
+
+      t = build_int_2 (result, 0);
+      TREE_TYPE (t) = TREE_TYPE (exp);
+      return t;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if EXPR is the real constant contained in VALUE.  */
+
+static bool
+real_dconstp (tree expr, const REAL_VALUE_TYPE *value)
+{
+  STRIP_NOPS (expr);
+
+  return ((TREE_CODE (expr) == REAL_CST
+           && ! TREE_CONSTANT_OVERFLOW (expr)
+           && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value))
+          || (TREE_CODE (expr) == COMPLEX_CST
+              && real_dconstp (TREE_REALPART (expr), value)
+              && real_zerop (TREE_IMAGPART (expr))));
+}
+
+/* A subroutine of fold_builtin to fold the various logarithmic
+   functions.  EXP is the CALL_EXPR of a call to a builtin log*
+   function.  VALUE is the base of the log* function.  */
+
+static tree
+fold_builtin_logarithm (tree exp, 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);
+       
+      /* Optimize log*(1.0) = 0.0.  */
+      if (real_onep (arg))
+       return build_real (type, dconst0);
+
+      /* Optimize logN(N) = 1.0.  If N can't be truncated to MODE
+         exactly, then only do this if flag_unsafe_math_optimizations.  */
+      if (exact_real_truncate (TYPE_MODE (type), value)
+         || flag_unsafe_math_optimizations)
+        {
+         const REAL_VALUE_TYPE value_truncate =
+           real_value_truncate (TYPE_MODE (type), *value);
+         if (real_dconstp (arg, &value_truncate))
+           return build_real (type, dconst1);
+       }
+      
+      /* Special case, optimize logN(expN(x)) = x.  */
+      if (flag_unsafe_math_optimizations
+         && ((value == &dconste
+              && (fcode == BUILT_IN_EXP
+                  || fcode == BUILT_IN_EXPF
+                  || fcode == BUILT_IN_EXPL))
+             || (value == &dconst2
+                 && (fcode == BUILT_IN_EXP2
+                     || fcode == BUILT_IN_EXP2F
+                     || fcode == BUILT_IN_EXP2L))
+             || (value == &dconst10
+                 && (fcode == BUILT_IN_EXP10
+                     || fcode == BUILT_IN_EXP10F
+                     || fcode == BUILT_IN_EXP10L))))
+       return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1)));
+
+      /* Optimize log*(func()) for various exponential functions.  We
+         want to determine the value "x" and the power "exponent" in
+         order to transform logN(x**exponent) into exponent*logN(x).  */
+      if (flag_unsafe_math_optimizations)
+        {
+         tree exponent = 0, x = 0;
+         
+         switch (fcode)
+         {
+         case BUILT_IN_EXP:
+         case BUILT_IN_EXPF:
+         case BUILT_IN_EXPL:
+           /* Prepare to do logN(exp(exponent) -> exponent*logN(e).  */
+           x = build_real (type,
+                           real_value_truncate (TYPE_MODE (type), dconste));
+           exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
+           break;
+         case BUILT_IN_EXP2:
+         case BUILT_IN_EXP2F:
+         case BUILT_IN_EXP2L:
+           /* Prepare to do logN(exp2(exponent) -> exponent*logN(2).  */
+           x = build_real (type, dconst2);
+           exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
+           break;
+         case BUILT_IN_EXP10:
+         case BUILT_IN_EXP10F:
+         case BUILT_IN_EXP10L:
+         case BUILT_IN_POW10:
+         case BUILT_IN_POW10F:
+         case BUILT_IN_POW10L:
+           /* Prepare to do logN(exp10(exponent) -> exponent*logN(10).  */
+           x = build_real (type, dconst10);
+           exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
+           break;
+         case BUILT_IN_SQRT:
+         case BUILT_IN_SQRTF:
+         case BUILT_IN_SQRTL:
+           /* Prepare to do logN(sqrt(x) -> 0.5*logN(x).  */
+           x = TREE_VALUE (TREE_OPERAND (arg, 1));
+           exponent = build_real (type, dconsthalf);
+           break;
+         case BUILT_IN_CBRT:
+         case BUILT_IN_CBRTF:
+         case BUILT_IN_CBRTL:
+           /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x).  */
+           x = TREE_VALUE (TREE_OPERAND (arg, 1));
+           exponent = build_real (type, real_value_truncate (TYPE_MODE (type),
+                                                             dconstthird));
+           break;
+         case BUILT_IN_POW:
+         case BUILT_IN_POWF:
+         case BUILT_IN_POWL:
+           /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x).  */
+           x = TREE_VALUE (TREE_OPERAND (arg, 1));
+           exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
+           break;
+         default:
+           break;
+         }
+
+         /* Now perform the optimization.  */
+         if (x && exponent)
+           {
+             tree logfn;
+             arglist = build_tree_list (NULL_TREE, x);
+             logfn = build_function_call_expr (fndecl, arglist);
+             return fold (build (MULT_EXPR, type, exponent, logfn));
+           }
+       }
+    }
+
+  return 0;
+}
+         
+/* A subroutine of fold_builtin to fold the various exponent
+   functions.  EXP is the CALL_EXPR of a call to a builtin function.
+   VALUE is the value which will be raised to a power.  */
+
+static tree
+fold_builtin_exponent (tree exp, 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);
+
+      /* Optimize exp*(0.0) = 1.0.  */
+      if (real_zerop (arg))
+       return build_real (type, dconst1);
+
+      /* Optimize expN(1.0) = N.  */
+      if (real_onep (arg))
+        {
+         REAL_VALUE_TYPE cst;
+
+         real_convert (&cst, TYPE_MODE (type), value);
+         return build_real (type, cst);
+       }
+
+      /* Attempt to evaluate expN(integer) at compile-time.  */
+      if (flag_unsafe_math_optimizations
+         && TREE_CODE (arg) == REAL_CST
+         && ! TREE_CONSTANT_OVERFLOW (arg))
+        {
+         REAL_VALUE_TYPE cint;
+         REAL_VALUE_TYPE c;
+         HOST_WIDE_INT n;
+
+         c = TREE_REAL_CST (arg);
+         n = real_to_integer (&c);
+         real_from_integer (&cint, VOIDmode, n,
+                            n < 0 ? -1 : 0, 0);
+         if (real_identical (&c, &cint))
+           {
+             REAL_VALUE_TYPE x;
+
+             real_powi (&x, TYPE_MODE (type), value, n);
+             return build_real (type, x);
+           }
+       }
+
+      /* Optimize expN(logN(x)) = x.  */
+      if (flag_unsafe_math_optimizations)
+        {
+         const enum built_in_function fcode = builtin_mathfn_code (arg);
+
+         if ((value == &dconste
+              && (fcode == BUILT_IN_LOG
+                  || fcode == BUILT_IN_LOGF
+                  || fcode == BUILT_IN_LOGL))
+             || (value == &dconst2
+                 && (fcode == BUILT_IN_LOG2
+                     || fcode == BUILT_IN_LOG2F
+                     || fcode == BUILT_IN_LOG2L))
+             || (value == &dconst10
+                 && (fcode == BUILT_IN_LOG10
+                     || fcode == BUILT_IN_LOG10F
+                     || fcode == BUILT_IN_LOG10L)))
+           return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1)));
+       }
+    }
+
+  return 0;
+}
+
+/* Fold function call to builtin memcpy.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_memcpy (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src, len;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
+    return omit_one_operand (TREE_TYPE (exp), 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 0;
+}
+
+/* Fold function call to builtin mempcpy.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_mempcpy (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src, len;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
+    return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+  /* If SRC and DEST are the same (and not volatile), return DEST+LEN.  */
+  if (operand_equal_p (src, dest, 0))
+    {
+      tree temp = convert (TREE_TYPE (dest), len);
+      temp = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+      return convert (TREE_TYPE (exp), temp);
+    }
+
+  return 0;
+}
+
+/* Fold function call to builtin memmove.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_memmove (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src, len;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
+    return omit_one_operand (TREE_TYPE (exp), 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 0;
+}
+
+/* Fold function call to builtin strcpy.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_strcpy (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+
+  /* If SRC and DEST are the same (and not volatile), return DEST.  */
+  if (operand_equal_p (src, dest, 0))
+    return convert (TREE_TYPE (exp), dest);
+
+  return 0;
+}
+
+/* Fold function call to builtin strncpy.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_strncpy (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src, len;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
+    return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+  return 0;
+}
+
+/* Fold function call to builtin memcmp.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_memcmp (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg1, arg2, len;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  arg1 = TREE_VALUE (arglist);
+  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    {
+      tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2);
+      return omit_one_operand (TREE_TYPE (exp), temp, arg1);
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len);
+
+  return 0;
+}
+
+/* Fold function call to builtin strcmp.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_strcmp (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg1, arg2;
+  const char *p1, *p2;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return 0;
+
+  arg1 = TREE_VALUE (arglist);
+  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    return convert (TREE_TYPE (exp), integer_zero_node);
+
+  p1 = c_getstr (arg1);
+  p2 = c_getstr (arg2);
+
+  if (p1 && p2)
+    {
+      tree temp;
+      const int i = strcmp (p1, p2);
+      if (i < 0)
+       temp = integer_minus_one_node;
+      else if (i > 0)
+       temp = integer_one_node;
+      else
+       temp = integer_zero_node;
+      return convert (TREE_TYPE (exp), temp);
+    }
+
+  return 0;
+}
+
+/* Fold function call to builtin strncmp.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_strncmp (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg1, arg2, len;
+  const char *p1, *p2;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  arg1 = TREE_VALUE (arglist);
+  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    {
+      tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2);
+      return omit_one_operand (TREE_TYPE (exp), temp, arg1);
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len);
+
+  p1 = c_getstr (arg1);
+  p2 = c_getstr (arg2);
+
+  if (host_integerp (len, 1) && p1 && p2)
+    {
+      tree temp;
+      const int i = strncmp (p1, p2, tree_low_cst (len, 1));
+      if (i < 0)
+       temp = integer_minus_one_node;
+      else if (i > 0)
+       temp = integer_one_node;
+      else
+       temp = integer_zero_node;
+      return convert (TREE_TYPE (exp), temp);
+    }
+
+  return 0;
+}
+
 /* Used by constant folding to eliminate some builtin calls early.  EXP is
    the CALL_EXPR of a call to a builtin function.  */
 
 tree
 fold_builtin (tree exp)
 {
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  tree fndecl = get_callee_fndecl (exp);
   tree arglist = TREE_OPERAND (exp, 1);
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
 
@@ -5222,7 +6514,7 @@ fold_builtin (tree exp)
     case BUILT_IN_STRLEN:
       if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
        {
-         tree len = c_strlen (TREE_VALUE (arglist));
+         tree len = c_strlen (TREE_VALUE (arglist), 0);
          if (len)
            {
              /* Convert from the internal "sizetype" type to "size_t".  */
@@ -5335,107 +6627,32 @@ fold_builtin (tree exp)
     case BUILT_IN_EXP:
     case BUILT_IN_EXPF:
     case BUILT_IN_EXPL:
-      if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
-       {
-         enum built_in_function fcode;
-         tree arg = TREE_VALUE (arglist);
-
-         /* Optimize exp(0.0) = 1.0.  */
-         if (real_zerop (arg))
-           return build_real (type, dconst1);
-
-         /* Optimize exp(1.0) = e.  */
-         if (real_onep (arg))
-           {
-             REAL_VALUE_TYPE cst;
-
-             if (! builtin_dconsts_init)
-               init_builtin_dconsts ();
-             real_convert (&cst, TYPE_MODE (type), &dconste);
-             return build_real (type, cst);
-           }
-
-         /* Attempt to evaluate exp at compile-time.  */
-         if (flag_unsafe_math_optimizations
-             && TREE_CODE (arg) == REAL_CST
-             && ! TREE_CONSTANT_OVERFLOW (arg))
-           {
-             REAL_VALUE_TYPE cint;
-             REAL_VALUE_TYPE c;
-             HOST_WIDE_INT n;
-
-             c = TREE_REAL_CST (arg);
-             n = real_to_integer (&c);
-             real_from_integer (&cint, VOIDmode, n,
-                                n < 0 ? -1 : 0, 0);
-             if (real_identical (&c, &cint))
-               {
-                 REAL_VALUE_TYPE x;
-
-                 if (! builtin_dconsts_init)
-                   init_builtin_dconsts ();
-                 real_powi (&x, TYPE_MODE (type), &dconste, n);
-                 return build_real (type, x);
-               }
-           }
-
-         /* Optimize exp(log(x)) = x.  */
-         fcode = builtin_mathfn_code (arg);
-         if (flag_unsafe_math_optimizations
-             && (fcode == BUILT_IN_LOG
-                 || fcode == BUILT_IN_LOGF
-                 || fcode == BUILT_IN_LOGL))
-           return TREE_VALUE (TREE_OPERAND (arg, 1));
-       }
-      break;
-
+      return fold_builtin_exponent (exp, &dconste);
+    case BUILT_IN_EXP2:
+    case BUILT_IN_EXP2F:
+    case BUILT_IN_EXP2L:
+      return fold_builtin_exponent (exp, &dconst2);
+    case BUILT_IN_EXP10:
+    case BUILT_IN_EXP10F:
+    case BUILT_IN_EXP10L:
+    case BUILT_IN_POW10:
+    case BUILT_IN_POW10F:
+    case BUILT_IN_POW10L:
+      return fold_builtin_exponent (exp, &dconst10);
     case BUILT_IN_LOG:
     case BUILT_IN_LOGF:
     case BUILT_IN_LOGL:
-      if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
-       {
-         enum built_in_function fcode;
-         tree arg = TREE_VALUE (arglist);
-
-         /* Optimize log(1.0) = 0.0.  */
-         if (real_onep (arg))
-           return build_real (type, dconst0);
-
-         /* Optimize log(exp(x)) = x.  */
-         fcode = builtin_mathfn_code (arg);
-         if (flag_unsafe_math_optimizations
-             && (fcode == BUILT_IN_EXP
-                 || fcode == BUILT_IN_EXPF
-                 || fcode == BUILT_IN_EXPL))
-           return TREE_VALUE (TREE_OPERAND (arg, 1));
-
-         /* Optimize log(sqrt(x)) = log(x)*0.5.  */
-         if (flag_unsafe_math_optimizations
-             && (fcode == BUILT_IN_SQRT
-                 || fcode == BUILT_IN_SQRTF
-                 || fcode == BUILT_IN_SQRTL))
-           {
-             tree logfn = build_function_call_expr (fndecl,
-                                                    TREE_OPERAND (arg, 1));
-             return fold (build (MULT_EXPR, type, logfn,
-                                 build_real (type, dconsthalf)));
-           }
-
-         /* Optimize log(pow(x,y)) = y*log(x).  */
-          if (flag_unsafe_math_optimizations
-             && (fcode == BUILT_IN_POW
-                 || fcode == BUILT_IN_POWF
-                 || fcode == BUILT_IN_POWL))
-           {
-             tree arg0, arg1, logfn;
-
-             arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
-             arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
-             arglist = build_tree_list (NULL_TREE, arg0);
-             logfn = build_function_call_expr (fndecl, arglist);
-             return fold (build (MULT_EXPR, type, arg1, logfn));
-           }
-       }
+      return fold_builtin_logarithm (exp, &dconste);
+      break;
+    case BUILT_IN_LOG2:
+    case BUILT_IN_LOG2F:
+    case BUILT_IN_LOG2L:
+      return fold_builtin_logarithm (exp, &dconst2);
+      break;
+    case BUILT_IN_LOG10:
+    case BUILT_IN_LOG10F:
+    case BUILT_IN_LOG10L:
+      return fold_builtin_logarithm (exp, &dconst10);
       break;
 
     case BUILT_IN_TAN:
@@ -5476,8 +6693,6 @@ fold_builtin (tree exp)
            {
              REAL_VALUE_TYPE cst;
 
-             if (! builtin_dconsts_init)
-               init_builtin_dconsts ();
              real_convert (&cst, TYPE_MODE (type), &dconstpi);
              cst.exp -= 2;
              return build_real (type, cst);
@@ -5519,28 +6734,6 @@ fold_builtin (tree exp)
                                    build_real (type, dconst1),
                                    arg0));
 
-             /* Optimize pow(x,2.0) = x*x.  */
-             if (REAL_VALUES_EQUAL (c, dconst2)
-                 && (*lang_hooks.decls.global_bindings_p) () == 0
-                 && ! CONTAINS_PLACEHOLDER_P (arg0))
-               {
-                 arg0 = save_expr (arg0);
-                 return fold (build (MULT_EXPR, type, arg0, arg0));
-               }
-
-             /* Optimize pow(x,-2.0) = 1.0/(x*x).  */
-             if (flag_unsafe_math_optimizations
-                 && REAL_VALUES_EQUAL (c, dconstm2)
-                 && (*lang_hooks.decls.global_bindings_p) () == 0
-                 && ! CONTAINS_PLACEHOLDER_P (arg0))
-               {
-                 arg0 = save_expr (arg0);
-                 return fold (build (RDIV_EXPR, type,
-                                     build_real (type, dconst1),
-                                     fold (build (MULT_EXPR, type,
-                                                  arg0, arg0))));
-               }
-
              /* Optimize pow(x,0.5) = sqrt(x).  */
              if (flag_unsafe_math_optimizations
                  && REAL_VALUES_EQUAL (c, dconsthalf))
@@ -5655,12 +6848,18 @@ fold_builtin (tree exp)
     case BUILT_IN_FLOOR:
     case BUILT_IN_FLOORF:
     case BUILT_IN_FLOORL:
+      return fold_builtin_floor (exp);
+
     case BUILT_IN_CEIL:
     case BUILT_IN_CEILF:
     case BUILT_IN_CEILL:
+      return fold_builtin_ceil (exp);
+
     case BUILT_IN_TRUNC:
     case BUILT_IN_TRUNCF:
     case BUILT_IN_TRUNCL:
+      return fold_builtin_trunc (exp);
+
     case BUILT_IN_ROUND:
     case BUILT_IN_ROUNDF:
     case BUILT_IN_ROUNDL:
@@ -5669,6 +6868,47 @@ fold_builtin (tree exp)
     case BUILT_IN_NEARBYINTL:
       return fold_trunc_transparent_mathfn (exp);
 
+    case BUILT_IN_FFS:
+    case BUILT_IN_FFSL:
+    case BUILT_IN_FFSLL:
+    case BUILT_IN_CLZ:
+    case BUILT_IN_CLZL:
+    case BUILT_IN_CLZLL:
+    case BUILT_IN_CTZ:
+    case BUILT_IN_CTZL:
+    case BUILT_IN_CTZLL:
+    case BUILT_IN_POPCOUNT:
+    case BUILT_IN_POPCOUNTL:
+    case BUILT_IN_POPCOUNTLL:
+    case BUILT_IN_PARITY:
+    case BUILT_IN_PARITYL:
+    case BUILT_IN_PARITYLL:
+      return fold_builtin_bitop (exp);
+
+    case BUILT_IN_MEMCPY:
+      return fold_builtin_memcpy (exp);
+
+    case BUILT_IN_MEMPCPY:
+      return fold_builtin_mempcpy (exp);
+
+    case BUILT_IN_MEMMOVE:
+      return fold_builtin_memmove (exp);
+
+    case BUILT_IN_STRCPY:
+      return fold_builtin_strcpy (exp);
+
+    case BUILT_IN_STRNCPY:
+      return fold_builtin_strncpy (exp);
+
+    case BUILT_IN_MEMCMP:
+      return fold_builtin_memcmp (exp);
+
+    case BUILT_IN_STRCMP:
+      return fold_builtin_strcmp (exp);
+
+    case BUILT_IN_STRNCMP:
+      return fold_builtin_strncmp (exp);
+
     default:
       break;
     }
@@ -5686,7 +6926,6 @@ build_function_call_expr (tree fn, tree arglist)
   call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
   call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
                     call_expr, arglist);
-  TREE_SIDE_EFFECTS (call_expr) = 1;
   return fold (call_expr);
 }
 
@@ -5739,13 +6978,6 @@ validate_arglist (tree arglist, ...)
   return res;
 }
 
-/* Default version of target-specific builtin setup that does nothing.  */
-
-void
-default_init_builtins (void)
-{
-}
-
 /* Default target-specific builtin expander that does nothing.  */
 
 rtx