OSDN Git Service

gcc:
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 20ccda3..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);
@@ -1766,6 +1793,10 @@ 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))
@@ -1851,6 +1882,11 @@ 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))
@@ -2176,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.  */
@@ -2759,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;
 
@@ -2802,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);
@@ -2841,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;
 
@@ -3267,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);
@@ -3414,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);
@@ -4210,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;
 
@@ -4549,7 +4597,7 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
 
       if (target != const0_rtx)
        {
-         len = c_strlen (arg);
+         len = c_strlen (arg, 1);
          if (! len || TREE_CODE (len) != INTEGER_CST)
            return 0;
        }
@@ -5303,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;
+
+  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 && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+  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;
@@ -5417,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.  */
 
@@ -5441,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".  */
@@ -5874,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: