OSDN Git Service

2007-03-01 Paul Brook <paul@codesourcery.com>
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 67d9bf6..676b9ca 100644 (file)
@@ -1973,10 +1973,15 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
   bool errno_set = true;
   bool stable = true;
 
-  if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXP
-      || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPF
-      || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPL)
-    op1_type = INTEGER_TYPE;
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    CASE_FLT_FN (BUILT_IN_SCALBN):
+    CASE_FLT_FN (BUILT_IN_SCALBLN):
+    CASE_FLT_FN (BUILT_IN_LDEXP):
+      op1_type = INTEGER_TYPE;
+    default:
+      break;
+    }
 
   if (!validate_arglist (exp, REAL_TYPE, op1_type, VOID_TYPE))
     return NULL_RTX;
@@ -1990,6 +1995,15 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
       builtin_optab = pow_optab; break;
     CASE_FLT_FN (BUILT_IN_ATAN2):
       builtin_optab = atan2_optab; break;
+    CASE_FLT_FN (BUILT_IN_SCALB):
+      if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (exp)))->b != 2)
+       return 0;
+      builtin_optab = scalb_optab; break;
+    CASE_FLT_FN (BUILT_IN_SCALBN):
+    CASE_FLT_FN (BUILT_IN_SCALBLN):
+      if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (exp)))->b != 2)
+       return 0;
+    /* Fall through... */
     CASE_FLT_FN (BUILT_IN_LDEXP):
       builtin_optab = ldexp_optab; break;
     CASE_FLT_FN (BUILT_IN_FMOD):
@@ -5963,6 +5977,9 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
 
     CASE_FLT_FN (BUILT_IN_ATAN2):
     CASE_FLT_FN (BUILT_IN_LDEXP):
+    CASE_FLT_FN (BUILT_IN_SCALB):
+    CASE_FLT_FN (BUILT_IN_SCALBN):
+    CASE_FLT_FN (BUILT_IN_SCALBLN):
       if (! flag_unsafe_math_optimizations)
        break;
 
@@ -9000,6 +9017,142 @@ fold_builtin_carg (tree arg, tree type)
   return NULL_TREE;
 }
 
+/* Fold a call to builtin logb/ilogb.  */
+
+static tree
+fold_builtin_logb (tree arg, tree rettype)
+{
+  if (! validate_arg (arg, REAL_TYPE))
+    return NULL_TREE;
+  
+  STRIP_NOPS (arg);
+      
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
+    {
+      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
+         
+      switch (value->cl)
+      {
+      case rvc_nan:
+      case rvc_inf:
+       /* If arg is Inf or NaN and we're logb, return it.  */
+       if (TREE_CODE (rettype) == REAL_TYPE)
+         return fold_convert (rettype, arg);
+       /* Fall through... */
+      case rvc_zero:
+       /* Zero may set errno and/or raise an exception for logb, also
+          for ilogb we don't know FP_ILOGB0.  */
+       return NULL_TREE;
+      case rvc_normal:
+       /* For normal numbers, proceed iff radix == 2.  In GCC,
+          normalized significands are in the range [0.5, 1.0).  We
+          want the exponent as if they were [1.0, 2.0) so get the
+          exponent and subtract 1.  */
+       if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
+         return fold_convert (rettype, build_int_cst (NULL_TREE,
+                                                      REAL_EXP (value)-1));
+       break;
+      }
+    }
+  
+  return NULL_TREE;
+}
+
+/* Fold a call to builtin significand, if radix == 2.  */
+
+static tree
+fold_builtin_significand (tree arg, tree rettype)
+{
+  if (! validate_arg (arg, REAL_TYPE))
+    return NULL_TREE;
+  
+  STRIP_NOPS (arg);
+      
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
+    {
+      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
+         
+      switch (value->cl)
+      {
+      case rvc_zero:
+      case rvc_nan:
+      case rvc_inf:
+       /* If arg is +-0, +-Inf or +-NaN, then return it.  */
+       return fold_convert (rettype, arg);
+      case rvc_normal:
+       /* For normal numbers, proceed iff radix == 2.  */
+       if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
+         {
+           REAL_VALUE_TYPE result = *value;
+           /* In GCC, normalized significands are in the range [0.5,
+              1.0).  We want them to be [1.0, 2.0) so set the
+              exponent to 1.  */
+           SET_REAL_EXP (&result, 1);
+           return build_real (rettype, result);
+         }
+       break;
+      }
+    }
+  
+  return NULL_TREE;
+}
+
+/* Fold a call to builtin frexp, we can assume the base is 2.  */
+
+static tree
+fold_builtin_frexp (tree arg0, tree arg1, tree rettype)
+{
+  if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
+    return NULL_TREE;
+  
+  STRIP_NOPS (arg0);
+      
+  if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
+    return NULL_TREE;
+  
+  arg1 = build_fold_indirect_ref (arg1);
+
+  /* Proceed if a valid pointer type was passed in.  */
+  if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == integer_type_node)
+    {
+      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
+      tree frac, exp;
+         
+      switch (value->cl)
+      {
+      case rvc_zero:
+       /* For +-0, return (*exp = 0, +-0).  */
+       exp = integer_zero_node;
+       frac = arg0;
+       break;
+      case rvc_nan:
+      case rvc_inf:
+       /* For +-NaN or +-Inf, *exp is unspecified, return arg0.  */
+       return omit_one_operand (rettype, arg0, arg1);
+      case rvc_normal:
+       {
+         /* Since the frexp function always expects base 2, and in
+            GCC normalized significands are already in the range
+            [0.5, 1.0), we have exactly what frexp wants.  */
+         REAL_VALUE_TYPE frac_rvt = *value;
+         SET_REAL_EXP (&frac_rvt, 0);
+         frac = build_real (rettype, frac_rvt);
+         exp = build_int_cst (NULL_TREE, REAL_EXP (value));
+       }
+       break;
+      default:
+       gcc_unreachable ();
+      }
+               
+      /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
+      arg1 = fold_build2 (MODIFY_EXPR, rettype, arg1, exp);
+      TREE_SIDE_EFFECTS (arg1) = 1;
+      return fold_build2 (COMPOUND_EXPR, rettype, arg1, frac);
+    }
+
+  return NULL_TREE;
+}
+
 /* Fold a call to builtin ldexp or scalbn/scalbln.  If LDEXP is true
    then we can assume the base is two.  If it's false, then we have to
    check the mode of the TYPE parameter in certain cases.  */
@@ -9061,6 +9214,62 @@ fold_builtin_load_exponent (tree arg0, tree arg1, tree type, bool ldexp)
   return NULL_TREE;
 }
 
+/* Fold a call to builtin modf.  */
+
+static tree
+fold_builtin_modf (tree arg0, tree arg1, tree rettype)
+{
+  if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
+    return NULL_TREE;
+  
+  STRIP_NOPS (arg0);
+      
+  if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
+    return NULL_TREE;
+  
+  arg1 = build_fold_indirect_ref (arg1);
+
+  /* Proceed if a valid pointer type was passed in.  */
+  if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == TYPE_MAIN_VARIANT (rettype))
+    {
+      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
+      REAL_VALUE_TYPE trunc, frac;
+
+      switch (value->cl)
+      {
+      case rvc_nan:
+      case rvc_zero:
+       /* For +-NaN or +-0, return (*arg1 = arg0, arg0).  */
+       trunc = frac = *value;
+       break;
+      case rvc_inf:
+       /* For +-Inf, return (*arg1 = arg0, +-0).  */
+       frac = dconst0;
+       frac.sign = value->sign;
+       trunc = *value;
+       break;
+      case rvc_normal:
+       /* Return (*arg1 = trunc(arg0), arg0-trunc(arg0)).  */
+       real_trunc (&trunc, VOIDmode, value);
+       real_arithmetic (&frac, MINUS_EXPR, value, &trunc);
+       /* If the original number was negative and already
+          integral, then the fractional part is -0.0.  */
+       if (value->sign && frac.cl == rvc_zero)
+         frac.sign = value->sign;
+       break;
+      }
+             
+      /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
+      arg1 = fold_build2 (MODIFY_EXPR, rettype, arg1,
+                         build_real (rettype, trunc));
+      TREE_SIDE_EFFECTS (arg1) = 1;
+      return fold_build2 (COMPOUND_EXPR, rettype, arg1,
+                         build_real (rettype, frac));
+    }
+  
+  return NULL_TREE;
+}
+
 /* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
    ARG is the argument for the call.  */
 
@@ -9453,6 +9662,13 @@ fold_builtin_1 (tree fndecl, tree arg0, bool ignore)
     CASE_FLT_FN (BUILT_IN_SIGNBIT):
       return fold_builtin_signbit (arg0, type);
 
+    CASE_FLT_FN (BUILT_IN_SIGNIFICAND):
+      return fold_builtin_significand (arg0, type);
+
+    CASE_FLT_FN (BUILT_IN_ILOGB):
+    CASE_FLT_FN (BUILT_IN_LOGB):
+      return fold_builtin_logb (arg0, type);
+
     case BUILT_IN_ISASCII:
       return fold_builtin_isascii (arg0);
 
@@ -9527,6 +9743,12 @@ fold_builtin_2 (tree fndecl, tree arg0, tree arg1, bool ignore)
     CASE_FLT_FN (BUILT_IN_SCALBLN):
       return fold_builtin_load_exponent (arg0, arg1, type, /*ldexp=*/false);
 
+    CASE_FLT_FN (BUILT_IN_FREXP):
+      return fold_builtin_frexp (arg0, arg1, type);
+
+    CASE_FLT_FN (BUILT_IN_MODF):
+      return fold_builtin_modf (arg0, arg1, type);
+
     case BUILT_IN_BZERO:
       return fold_builtin_bzero (arg0, arg1, ignore);
 
@@ -9902,56 +10124,13 @@ build_function_call_expr (tree fndecl, tree arglist)
 {
   tree fntype = TREE_TYPE (fndecl);
   tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
-  return fold_builtin_call_list (TREE_TYPE (fntype), fn, arglist);
-}
-
-/* Construct a CALL_EXPR with type TYPE with FN as the function expression.
-   ARGLIST is a TREE_LIST of arguments.  */
-
-tree
-fold_builtin_call_list (tree type, tree fn, tree arglist)
-{
-  tree ret = NULL_TREE;
-  if (TREE_CODE (fn) == ADDR_EXPR)
-    {
-      tree fndecl = TREE_OPERAND (fn, 0);
-      if (TREE_CODE (fndecl) == FUNCTION_DECL
-         && DECL_BUILT_IN (fndecl))
-       {
-         /* FIXME: Don't use a list in this interface.  */
-         if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
-           {
-             ret = targetm.fold_builtin (fndecl, arglist, false);
-             if (ret)
-               return ret;
-           }
-         else
-           {
-             tree tail = arglist;
-             tree args[MAX_ARGS_TO_FOLD_BUILTIN];
-             int nargs;
-             tree exp;
-
-             for (nargs = 0; nargs < MAX_ARGS_TO_FOLD_BUILTIN; nargs++)
-               {
-                 if (!tail)
-                   break;
-                 args[nargs] = TREE_VALUE (tail);
-                 tail = TREE_CHAIN (tail);
-               }
-             if (nargs <= MAX_ARGS_TO_FOLD_BUILTIN)
-               {
-                 ret = fold_builtin_n (fndecl, args, nargs, false);
-                 if (ret)
-                   return ret;
-               }
-             exp = build_call_list (type, fn, arglist);
-             ret = fold_builtin_varargs (fndecl, exp, false);
-             return ret ? ret : exp;
-           }
-       }
-    }
-  return build_call_list (type, fn, arglist);
+  int n = list_length (arglist);
+  tree *argarray = (tree *) alloca (n * sizeof (tree));
+  int i;
+  
+  for (i = 0; i < n; i++, arglist = TREE_CHAIN (arglist))
+    argarray[i] = TREE_VALUE (arglist);
+  return fold_builtin_call_array (TREE_TYPE (fntype), fn, n, argarray);
 }
 
 /* Conveniently construct a function call expression.  FNDECL names the
@@ -9962,24 +10141,26 @@ tree
 build_call_expr (tree fndecl, int n, ...)
 {
   va_list ap;
-  tree ret;
   tree fntype = TREE_TYPE (fndecl);
   tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
+  tree *argarray = (tree *) alloca (n * sizeof (tree));
+  int i;
 
   va_start (ap, n);
-  ret = fold_builtin_call_valist (TREE_TYPE (fntype), fn, n, ap);
+  for (i = 0; i < n; i++)
+    argarray[i] = va_arg (ap, tree);
   va_end (ap);
-  return ret;
+  return fold_builtin_call_array (TREE_TYPE (fntype), fn, n, argarray);
 }
 
 /* Construct a CALL_EXPR with type TYPE with FN as the function expression.
-   N arguments are passed in the va_list AP.  */
+   N arguments are passed in the array ARGARRAY.  */
 
 tree
-fold_builtin_call_valist (tree type,
-                         tree fn,
-                         int n,
-                         va_list ap)
+fold_builtin_call_array (tree type,
+                        tree fn,
+                        int n,
+                        tree *argarray)
 {
   tree ret = NULL_TREE;
   int i;
@@ -9994,15 +10175,8 @@ fold_builtin_call_valist (tree type,
         if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
           {
             tree arglist = NULL_TREE;
-            va_list ap0;
-            va_copy (ap0, ap);
-            for (i = 0; i < n; i++)
-              {
-                tree arg = va_arg (ap0, tree);
-                arglist = tree_cons (NULL_TREE, arg, arglist);
-              }
-            va_end (ap0);
-            arglist = nreverse (arglist);
+           for (i = n - 1; i >= 0; i--)
+             arglist = tree_cons (NULL_TREE, argarray[i], arglist);
             ret = targetm.fold_builtin (fndecl, arglist, false);
             if (ret)
               return ret;
@@ -10011,25 +10185,19 @@ fold_builtin_call_valist (tree type,
           {
             /* First try the transformations that don't require consing up
                an exp.  */
-            tree args[MAX_ARGS_TO_FOLD_BUILTIN];
-            va_list ap0;
-            va_copy (ap0, ap);
-            for (i = 0; i < n; i++)
-              args[i] = va_arg (ap0, tree);
-            va_end (ap0);
-            ret = fold_builtin_n (fndecl, args, n, false);
+            ret = fold_builtin_n (fndecl, argarray, n, false);
             if (ret)
               return ret;
           }
 
         /* If we got this far, we need to build an exp.  */
-        exp = build_call_valist (type, fn, n, ap);
+        exp = build_call_array (type, fn, n, argarray);
         ret = fold_builtin_varargs (fndecl, exp, false);
         return ret ? ret : exp;
       }
   }
 
-  return build_call_valist (type, fn, n, ap);
+  return build_call_array (type, fn, n, argarray);
 }
 
 /* Construct a new CALL_EXPR using the tail of the argument list of EXP