diff options
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r-- | gcc/builtins.c | 303 |
1 files changed, 227 insertions, 76 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index e1c2deef647..676b9caba66 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -9017,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. */ @@ -9078,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. */ @@ -9470,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); @@ -9544,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); @@ -9919,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 @@ -9979,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; @@ -10011,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; @@ -10028,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 |