OSDN Git Service

Index: ChangeLog
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 372c0c2..9471215 100644 (file)
@@ -85,7 +85,7 @@ 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 *);
@@ -149,12 +149,16 @@ 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);
+static tree fold_builtin_trunc (tree);
+static tree fold_builtin_floor (tree);
+static tree fold_builtin_ceil (tree);
 
 /* Initialize mathematical constants for constant folding builtins.
    These constants need to be given to at least 160 bits precision.  */
@@ -242,19 +246,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;
@@ -1204,7 +1231,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);
@@ -1701,7 +1728,7 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
   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;
@@ -1766,30 +1793,24 @@ expand_builtin_mathfn (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 and compute the argument.  */
-  if (errno_set)
-    switch (TREE_CODE (arg))
-      {
-      case VAR_DECL:
-      case PARM_DECL:
-      case SAVE_EXPR:
-      case REAL_CST:
-       break;
-
-      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);
 
@@ -1800,13 +1821,13 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
      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 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)
@@ -1834,7 +1855,7 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
   rtx op0, op1, insns;
   tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
   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 +1882,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 +1925,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)
@@ -2068,7 +2076,7 @@ powi_cost (HOST_WIDE_INT n)
        }
     }
   
-  return result + powi_lookup_cost (n, cache);
+  return result + powi_lookup_cost (val, cache);
 }
 
 /* Recursive subroutine of expand_powi.  This function takes the array,
@@ -2204,10 +2212,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.  */
@@ -2787,7 +2807,7 @@ expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode)
     return 0;
 
   src = TREE_VALUE (TREE_CHAIN (arglist));
-  len = c_strlen (src);
+  len = c_strlen (src, 1);
   if (len == 0 || TREE_SIDE_EFFECTS (len))
     return 0;
 
@@ -2830,7 +2850,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);
@@ -2869,7 +2889,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;
 
@@ -3295,8 +3315,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);
@@ -3442,8 +3462,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);
@@ -4238,7 +4258,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;
 
@@ -4329,7 +4349,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);
     }
 
@@ -4520,8 +4540,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;
 
@@ -4540,36 +4560,33 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
   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))
@@ -4580,25 +4597,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;
@@ -5338,19 +5351,118 @@ 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
+/* Return true if the floating point expression T has an integer value.
+   We also allow +Inf, -Inf and NaN to be considered integer values.  */
+
+static bool
+integer_valued_real_p (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    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;
+
+       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 = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
   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;
 
-  if (optimize && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+  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 (TREE_VALUE (arglist));
+      tree arg0 = strip_float_extensions (arg);
       tree ftype = TREE_TYPE (exp);
       tree newtype = TREE_TYPE (arg0);
       tree decl;
@@ -5452,6 +5564,97 @@ 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);
+}
+
 /* Used by constant folding to eliminate some builtin calls early.  EXP is
    the CALL_EXPR of a call to a builtin function.  */
 
@@ -5476,7 +5679,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".  */
@@ -5909,12 +6112,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: