+ case BUILT_IN_CABS:
+ case BUILT_IN_CABSF:
+ case BUILT_IN_CABSL:
+ return fold_builtin_cabs (arglist, type);
+
+ case BUILT_IN_SQRT:
+ case BUILT_IN_SQRTF:
+ case BUILT_IN_SQRTL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ enum built_in_function fcode;
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize sqrt of constant value. */
+ if (TREE_CODE (arg) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE r, x;
+
+ x = TREE_REAL_CST (arg);
+ if (real_sqrt (&r, TYPE_MODE (type), &x)
+ || (!flag_trapping_math && !flag_errno_math))
+ return build_real (type, r);
+ }
+
+ /* Optimize sqrt(expN(x)) = expN(x*0.5). */
+ fcode = builtin_mathfn_code (arg);
+ if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+ arg = fold (build (MULT_EXPR, type,
+ TREE_VALUE (TREE_OPERAND (arg, 1)),
+ build_real (type, dconsthalf)));
+ arglist = build_tree_list (NULL_TREE, arg);
+ return build_function_call_expr (expfn, arglist);
+ }
+
+ /* Optimize sqrt(Nroot(x)) -> pow(x,1/(2*N)). */
+ if (flag_unsafe_math_optimizations && BUILTIN_ROOT_P (fcode))
+ {
+ tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+
+ if (powfn)
+ {
+ tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+ tree tree_root;
+ /* The inner root was either sqrt or cbrt. */
+ REAL_VALUE_TYPE dconstroot =
+ BUILTIN_SQRT_P (fcode) ? dconsthalf : dconstthird;
+
+ /* Adjust for the outer root. */
+ SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
+ dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
+ tree_root = build_real (type, dconstroot);
+ arglist = tree_cons (NULL_TREE, arg0,
+ build_tree_list (NULL_TREE, tree_root));
+ return build_function_call_expr (powfn, arglist);
+ }
+ }
+
+ /* Optimize sqrt(pow(x,y)) = pow(x,y*0.5). */
+ if (flag_unsafe_math_optimizations
+ && (fcode == BUILT_IN_POW
+ || fcode == BUILT_IN_POWF
+ || fcode == BUILT_IN_POWL))
+ {
+ tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+ tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+ tree arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
+ tree narg1 = fold (build (MULT_EXPR, type, arg1,
+ build_real (type, dconsthalf)));
+ arglist = tree_cons (NULL_TREE, arg0,
+ build_tree_list (NULL_TREE, narg1));
+ return build_function_call_expr (powfn, arglist);
+ }
+ }
+ break;
+
+ case BUILT_IN_CBRT:
+ case BUILT_IN_CBRTF:
+ case BUILT_IN_CBRTL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree arg = TREE_VALUE (arglist);
+ const enum built_in_function fcode = builtin_mathfn_code (arg);
+
+ /* Optimize cbrt of constant value. */
+ if (real_zerop (arg) || real_onep (arg) || real_minus_onep (arg))
+ return arg;
+
+ /* Optimize cbrt(expN(x)) -> expN(x/3). */
+ if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+ const REAL_VALUE_TYPE third_trunc =
+ real_value_truncate (TYPE_MODE (type), dconstthird);
+ arg = fold (build (MULT_EXPR, type,
+ TREE_VALUE (TREE_OPERAND (arg, 1)),
+ build_real (type, third_trunc)));
+ arglist = build_tree_list (NULL_TREE, arg);
+ return build_function_call_expr (expfn, arglist);
+ }
+
+ /* Optimize cbrt(sqrt(x)) -> pow(x,1/6). */
+ /* We don't optimize cbrt(cbrt(x)) -> pow(x,1/9) because if
+ x is negative pow will error but cbrt won't. */
+ if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode))
+ {
+ tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+
+ if (powfn)
+ {
+ tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+ tree tree_root;
+ REAL_VALUE_TYPE dconstroot = dconstthird;
+
+ SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
+ dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
+ tree_root = build_real (type, dconstroot);
+ arglist = tree_cons (NULL_TREE, arg0,
+ build_tree_list (NULL_TREE, tree_root));
+ return build_function_call_expr (powfn, arglist);
+ }
+
+ }
+ }
+ break;
+
+ case BUILT_IN_SIN:
+ case BUILT_IN_SINF:
+ case BUILT_IN_SINL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize sin(0.0) = 0.0. */
+ if (real_zerop (arg))
+ return arg;
+ }
+ break;
+
+ case BUILT_IN_COS:
+ case BUILT_IN_COSF:
+ case BUILT_IN_COSL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize cos(0.0) = 1.0. */
+ if (real_zerop (arg))
+ return build_real (type, dconst1);
+
+ /* Optimize cos(-x) into cos(x). */
+ if (TREE_CODE (arg) == NEGATE_EXPR)
+ {
+ tree arglist = build_tree_list (NULL_TREE,
+ TREE_OPERAND (arg, 0));
+ return build_function_call_expr (fndecl, arglist);
+ }
+ }
+ break;
+
+ case BUILT_IN_EXP:
+ case BUILT_IN_EXPF:
+ case BUILT_IN_EXPL:
+ 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:
+ 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:
+ case BUILT_IN_TANF:
+ case BUILT_IN_TANL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ enum built_in_function fcode;
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize tan(0.0) = 0.0. */
+ if (real_zerop (arg))
+ return arg;
+
+ /* Optimize tan(atan(x)) = x. */
+ fcode = builtin_mathfn_code (arg);
+ if (flag_unsafe_math_optimizations
+ && (fcode == BUILT_IN_ATAN
+ || fcode == BUILT_IN_ATANF
+ || fcode == BUILT_IN_ATANL))
+ return TREE_VALUE (TREE_OPERAND (arg, 1));
+ }
+ break;
+
+ case BUILT_IN_ATAN:
+ case BUILT_IN_ATANF:
+ case BUILT_IN_ATANL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize atan(0.0) = 0.0. */
+ if (real_zerop (arg))
+ return arg;
+
+ /* Optimize atan(1.0) = pi/4. */
+ if (real_onep (arg))
+ {
+ REAL_VALUE_TYPE cst;
+
+ real_convert (&cst, TYPE_MODE (type), &dconstpi);
+ SET_REAL_EXP (&cst, REAL_EXP (&cst) - 2);
+ return build_real (type, cst);
+ }
+ }
+ break;
+
+ case BUILT_IN_POW:
+ case BUILT_IN_POWF:
+ case BUILT_IN_POWL:
+ if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ {
+ enum built_in_function fcode;
+ tree arg0 = TREE_VALUE (arglist);
+ tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ /* Optimize pow(1.0,y) = 1.0. */
+ if (real_onep (arg0))
+ return omit_one_operand (type, build_real (type, dconst1), arg1);
+
+ if (TREE_CODE (arg1) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg1))
+ {
+ REAL_VALUE_TYPE c;
+ c = TREE_REAL_CST (arg1);
+
+ /* Optimize pow(x,0.0) = 1.0. */
+ if (REAL_VALUES_EQUAL (c, dconst0))
+ return omit_one_operand (type, build_real (type, dconst1),
+ arg0);
+
+ /* Optimize pow(x,1.0) = x. */
+ if (REAL_VALUES_EQUAL (c, dconst1))
+ return arg0;
+
+ /* Optimize pow(x,-1.0) = 1.0/x. */
+ if (REAL_VALUES_EQUAL (c, dconstm1))
+ return fold (build (RDIV_EXPR, type,
+ build_real (type, dconst1),
+ arg0));
+
+ /* Optimize pow(x,0.5) = sqrt(x). */
+ if (flag_unsafe_math_optimizations
+ && REAL_VALUES_EQUAL (c, dconsthalf))
+ {
+ tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
+
+ if (sqrtfn != NULL_TREE)
+ {
+ tree arglist = build_tree_list (NULL_TREE, arg0);
+ return build_function_call_expr (sqrtfn, arglist);
+ }
+ }
+
+ /* Attempt to evaluate pow at compile-time. */
+ if (TREE_CODE (arg0) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg0))
+ {
+ REAL_VALUE_TYPE cint;
+ HOST_WIDE_INT n;
+
+ 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;
+ bool inexact;
+
+ x = TREE_REAL_CST (arg0);
+ inexact = real_powi (&x, TYPE_MODE (type), &x, n);
+ if (flag_unsafe_math_optimizations || !inexact)
+ return build_real (type, x);
+ }
+ }
+ }
+
+ /* Optimize pow(expN(x),y) = expN(x*y). */
+ fcode = builtin_mathfn_code (arg0);
+ if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
+ tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
+ arg = fold (build (MULT_EXPR, type, arg, arg1));
+ arglist = build_tree_list (NULL_TREE, arg);
+ return build_function_call_expr (expfn, arglist);
+ }
+
+ /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */
+ if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode))
+ {
+ tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1));
+ tree narg1 = fold (build (MULT_EXPR, type, arg1,
+ build_real (type, dconsthalf)));
+
+ arglist = tree_cons (NULL_TREE, narg0,
+ build_tree_list (NULL_TREE, narg1));
+ return build_function_call_expr (fndecl, arglist);
+ }
+
+ /* Optimize pow(pow(x,y),z) = pow(x,y*z). */
+ if (flag_unsafe_math_optimizations
+ && (fcode == BUILT_IN_POW
+ || fcode == BUILT_IN_POWF
+ || fcode == BUILT_IN_POWL))
+ {
+ tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1));
+ tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1)));
+ tree narg1 = fold (build (MULT_EXPR, type, arg01, arg1));
+ arglist = tree_cons (NULL_TREE, arg00,
+ build_tree_list (NULL_TREE, narg1));
+ return build_function_call_expr (fndecl, arglist);
+ }
+ }
+ break;
+
+ case BUILT_IN_INF:
+ case BUILT_IN_INFF:
+ case BUILT_IN_INFL:
+ return fold_builtin_inf (type, true);
+
+ case BUILT_IN_HUGE_VAL:
+ case BUILT_IN_HUGE_VALF:
+ case BUILT_IN_HUGE_VALL:
+ return fold_builtin_inf (type, false);
+
+ case BUILT_IN_NAN:
+ case BUILT_IN_NANF:
+ case BUILT_IN_NANL:
+ return fold_builtin_nan (arglist, type, true);
+
+ case BUILT_IN_NANS:
+ case BUILT_IN_NANSF:
+ case BUILT_IN_NANSL:
+ return fold_builtin_nan (arglist, type, false);
+
+ 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:
+ return fold_builtin_round (exp);
+
+ case BUILT_IN_NEARBYINT:
+ case BUILT_IN_NEARBYINTF:
+ case BUILT_IN_NEARBYINTL:
+ case BUILT_IN_RINT:
+ case BUILT_IN_RINTF:
+ case BUILT_IN_RINTL:
+ return fold_trunc_transparent_mathfn (exp);
+
+ case BUILT_IN_LROUND:
+ case BUILT_IN_LROUNDF:
+ case BUILT_IN_LROUNDL:
+ case BUILT_IN_LLROUND:
+ case BUILT_IN_LLROUNDF:
+ case BUILT_IN_LLROUNDL:
+ return fold_builtin_lround (exp);
+
+ case BUILT_IN_LRINT:
+ case BUILT_IN_LRINTF:
+ case BUILT_IN_LRINTL:
+ case BUILT_IN_LLRINT:
+ case BUILT_IN_LLRINTF:
+ case BUILT_IN_LLRINTL:
+ return fold_fixed_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);
+
+ case BUILT_IN_SIGNBIT:
+ case BUILT_IN_SIGNBITF:
+ case BUILT_IN_SIGNBITL:
+ return fold_builtin_signbit (exp);
+
+ case BUILT_IN_ISASCII:
+ return fold_builtin_isascii (arglist);
+
+ case BUILT_IN_TOASCII:
+ return fold_builtin_toascii (arglist);
+
+ case BUILT_IN_ISDIGIT:
+ return fold_builtin_isdigit (arglist);
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* A wrapper function for builtin folding that prevents warnings for
+ "statement without effect" and the like, caused by removing the
+ call node earlier than the warning is generated. */
+
+tree
+fold_builtin (tree exp)
+{
+ exp = fold_builtin_1 (exp);
+ if (exp)
+ {
+ /* ??? Don't clobber shared nodes such as integer_zero_node. */
+ if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c')
+ exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp);
+ TREE_NO_WARNING (exp) = 1;
+ }
+ return exp;
+}
+
+/* Conveniently construct a function call expression. */
+
+tree
+build_function_call_expr (tree fn, tree arglist)
+{
+ tree call_expr;
+
+ 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, NULL_TREE);
+ return fold (call_expr);
+}
+
+/* This function validates the types of a function call argument list
+ represented as a tree chain of parameters against a specified list
+ of tree_codes. If the last specifier is a 0, that represents an
+ ellipses, otherwise the last specifier must be a VOID_TYPE. */
+
+static int
+validate_arglist (tree arglist, ...)
+{
+ enum tree_code code;
+ int res = 0;
+ va_list ap;
+
+ va_start (ap, arglist);
+
+ do
+ {
+ code = va_arg (ap, enum tree_code);
+ switch (code)
+ {
+ case 0:
+ /* This signifies an ellipses, any further arguments are all ok. */
+ res = 1;
+ goto end;
+ case VOID_TYPE:
+ /* This signifies an endlink, if no arguments remain, return
+ true, otherwise return false. */
+ res = arglist == 0;
+ goto end;
+ default:
+ /* If no parameters remain or the parameter's code does not
+ match the specified code, return false. Otherwise continue
+ checking any remaining arguments. */
+ if (arglist == 0
+ || code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))))
+ goto end;
+ break;
+ }
+ arglist = TREE_CHAIN (arglist);
+ }
+ while (1);
+
+ /* We need gotos here since we can only have one VA_CLOSE in a
+ function. */
+ end: ;
+ va_end (ap);
+
+ return res;
+}
+
+/* Default target-specific builtin expander that does nothing. */
+
+rtx
+default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
+ rtx target ATTRIBUTE_UNUSED,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int ignore ATTRIBUTE_UNUSED)
+{
+ return NULL_RTX;
+}
+
+/* Returns true is EXP represents data that would potentially reside
+ in a readonly section. */
+
+static bool
+readonly_data_expr (tree exp)
+{
+ STRIP_NOPS (exp);
+
+ if (TREE_CODE (exp) == ADDR_EXPR)
+ return decl_readonly_section (TREE_OPERAND (exp, 0), 0);
+ else
+ return false;
+}
+
+/* Front-end to the simplify_builtin_XXX routines.
+
+ EXP is a call to a builtin function. If possible try to simplify
+ that into a constant, expression or call to a more efficient
+ builtin function.
+
+ If IGNORE is nonzero, then the result of this builtin function
+ call is ignored.
+
+ If simplification is possible, return the simplified tree, otherwise
+ return NULL_TREE. */
+
+tree
+simplify_builtin (tree exp, int ignore)
+{
+ 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 val;
+
+ switch (fcode)
+ {
+ case BUILT_IN_FPUTS:
+ val = simplify_builtin_fputs (arglist, ignore, 0, NULL_TREE);
+ break;
+ case BUILT_IN_FPUTS_UNLOCKED:
+ val = simplify_builtin_fputs (arglist, ignore, 1, NULL_TREE);
+ break;
+ case BUILT_IN_STRSTR:
+ val = simplify_builtin_strstr (arglist);
+ break;
+ case BUILT_IN_STRCAT:
+ val = simplify_builtin_strcat (arglist);
+ break;
+ case BUILT_IN_STRNCAT:
+ val = simplify_builtin_strncat (arglist);
+ break;
+ case BUILT_IN_STRSPN:
+ val = simplify_builtin_strspn (arglist);
+ break;
+ case BUILT_IN_STRCSPN:
+ val = simplify_builtin_strcspn (arglist);
+ break;
+ case BUILT_IN_STRCHR:
+ case BUILT_IN_INDEX:
+ val = simplify_builtin_strchr (arglist);
+ break;
+ case BUILT_IN_STRRCHR:
+ case BUILT_IN_RINDEX:
+ val = simplify_builtin_strrchr (arglist);
+ break;
+ case BUILT_IN_STRCPY:
+ val = simplify_builtin_strcpy (arglist, NULL_TREE);
+ break;
+ case BUILT_IN_STRNCPY:
+ val = simplify_builtin_strncpy (arglist, NULL_TREE);
+ break;
+ case BUILT_IN_STRCMP:
+ val = simplify_builtin_strcmp (arglist);
+ break;
+ case BUILT_IN_STRNCMP:
+ val = simplify_builtin_strncmp (arglist);
+ break;
+ case BUILT_IN_STRPBRK:
+ val = simplify_builtin_strpbrk (arglist);
+ break;
+ case BUILT_IN_BCMP:
+ case BUILT_IN_MEMCMP:
+ val = simplify_builtin_memcmp (arglist);
+ break;
+ case BUILT_IN_VA_START:
+ simplify_builtin_va_start (arglist);
+ val = NULL_TREE;
+ break;
+ case BUILT_IN_SPRINTF:
+ val = simplify_builtin_sprintf (arglist, ignore);
+ break;
+ case BUILT_IN_CONSTANT_P:
+ val = fold_builtin_constant_p (arglist);
+ /* Gimplification will pull the CALL_EXPR for the builtin out of
+ an if condition. When not optimizing, we'll not CSE it back.
+ To avoid link error types of regressions, return false now. */
+ if (!val && !optimize)
+ val = integer_zero_node;
+ break;
+ default:
+ val = NULL_TREE;
+ break;
+ }
+
+ if (val)
+ val = convert (TREE_TYPE (exp), val);
+ return val;
+}
+
+/* Simplify a call to the strstr builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+static tree
+simplify_builtin_strstr (tree arglist)
+{
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree fn;
+ const char *p1, *p2;
+
+ p2 = c_getstr (s2);
+ if (p2 == NULL)
+ return 0;
+
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ const char *r = strstr (p1, p2);
+
+ /* Return an offset into the constant string argument. */
+ if (r == NULL)
+ return integer_zero_node;
+ else
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1))));
+ }
+
+ if (p2[0] == '\0')
+ return s1;
+
+ if (p2[1] != '\0')
+ return 0;
+
+ fn = implicit_built_in_decls[BUILT_IN_STRCHR];
+ if (!fn)
+ return 0;
+
+ /* New argument list transforming strstr(s1, s2) to
+ strchr(s1, s2[0]). */
+ arglist = build_tree_list (NULL_TREE, build_int_2 (p2[0], 0));
+ arglist = tree_cons (NULL_TREE, s1, arglist);
+ return build_function_call_expr (fn, arglist);
+ }
+}
+
+/* Simplify a call to the strstr builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+static tree
+simplify_builtin_strchr (tree arglist)
+{
+ if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+ const char *p1;
+
+ if (TREE_CODE (s2) != INTEGER_CST)
+ return 0;
+
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ char c;
+ const char *r;
+
+ if (target_char_cast (s2, &c))
+ return 0;
+
+ r = strchr (p1, c);
+
+ if (r == NULL)
+ return integer_zero_node;
+
+ /* Return an offset into the constant string argument. */
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1))));
+ }
+
+ /* FIXME: Should use here strchrM optab so that ports can optimize
+ this. */
+ return 0;
+ }
+}
+
+/* Simplify a call to the strrchr builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+static tree
+simplify_builtin_strrchr (tree arglist)
+{
+ if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree fn;
+ const char *p1;
+
+ if (TREE_CODE (s2) != INTEGER_CST)
+ return 0;
+
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ char c;
+ const char *r;
+
+ if (target_char_cast (s2, &c))
+ return 0;
+
+ r = strrchr (p1, c);
+
+ if (r == NULL)
+ return integer_zero_node;
+
+ /* Return an offset into the constant string argument. */
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1))));
+ }
+
+ if (! integer_zerop (s2))
+ return 0;
+
+ fn = implicit_built_in_decls[BUILT_IN_STRCHR];
+ if (!fn)
+ return 0;
+
+ /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */
+ return build_function_call_expr (fn, arglist);
+ }
+}
+
+/* Simplify a call to the strpbrk builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+static tree
+simplify_builtin_strpbrk (tree arglist)
+{
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree fn;
+ const char *p1, *p2;
+
+ p2 = c_getstr (s2);
+ if (p2 == NULL)
+ return 0;
+
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ const char *r = strpbrk (p1, p2);
+
+ if (r == NULL)
+ return integer_zero_node;
+
+ /* Return an offset into the constant string argument. */
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1))));
+ }
+
+ if (p2[0] == '\0')
+ {
+ /* strpbrk(x, "") == NULL.
+ Evaluate and ignore the arguments in case they had
+ side-effects. */
+ return build (COMPOUND_EXPR, integer_type_node, s1,
+ integer_zero_node);
+ }
+
+ if (p2[1] != '\0')
+ return 0; /* Really call strpbrk. */
+
+ fn = implicit_built_in_decls[BUILT_IN_STRCHR];
+ if (!fn)
+ return 0;
+
+ /* New argument list transforming strpbrk(s1, s2) to
+ strchr(s1, s2[0]). */
+ arglist =
+ build_tree_list (NULL_TREE, build_int_2 (p2[0], 0));
+ arglist = tree_cons (NULL_TREE, s1, arglist);
+ return build_function_call_expr (fn, arglist);
+ }
+}
+
+/* Simplify a call to the strcpy builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+tree
+simplify_builtin_strcpy (tree arglist, tree len)
+{
+ tree fn;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+
+ fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ if (!fn)
+ return 0;
+
+ if (!len)
+ {
+ len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
+ if (!len)
+ return 0;
+ if (TREE_SIDE_EFFECTS (len))
+ return 0;
+ }
+
+ len = size_binop (PLUS_EXPR, len, ssize_int (1));
+ chainon (arglist, build_tree_list (NULL_TREE, len));
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Simplify a call to the strncpy builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+tree
+simplify_builtin_strncpy (tree arglist, tree slen)
+{
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ tree fn;
+
+ /* We must be passed a constant len parameter. */
+ if (TREE_CODE (len) != INTEGER_CST)
+ return 0;
+
+ /* If the len parameter is zero, return the dst parameter. */
+ if (integer_zerop (len))
+ {
+ /* Evaluate and ignore the src argument in case it has
+ side-effects and return the dst parameter. */
+ return build (COMPOUND_EXPR, TREE_TYPE (TREE_VALUE (arglist)),
+ TREE_VALUE (TREE_CHAIN (arglist)),
+ TREE_VALUE (arglist));
+ }
+
+ if (!slen)
+ slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 0);
+
+ /* Now, we must be passed a constant src ptr parameter. */
+ if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
+ return 0;
+
+ slen = size_binop (PLUS_EXPR, slen, ssize_int (1));
+
+ /* We do not support simplification of this case, though we do
+ support it when expanding trees into RTL. */
+ /* FIXME: generate a call to __builtin_memset. */
+ if (tree_int_cst_lt (slen, len))
+ return 0;
+
+ /* OK transform into builtin memcpy. */
+ fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ if (!fn)
+ return 0;
+ return build_function_call_expr (fn, arglist);
+ }
+}
+
+/* Simplify a call to the memcmp builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+static tree
+simplify_builtin_memcmp (tree arglist)
+{
+ 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 (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+ {
+ /* Evaluate and ignore arg1 and arg2 in case they have
+ side-effects. */
+ return build (COMPOUND_EXPR, integer_type_node, arg1,
+ build (COMPOUND_EXPR, integer_type_node,
+ arg2, integer_zero_node));
+ }
+
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ /* If all arguments are constant, and the value of len is not greater
+ than the lengths of arg1 and arg2, evaluate at compile-time. */
+ if (host_integerp (len, 1) && p1 && p2
+ && compare_tree_int (len, strlen (p1) + 1) <= 0
+ && compare_tree_int (len, strlen (p2) + 1) <= 0)
+ {
+ const int r = memcmp (p1, p2, tree_low_cst (len, 1));
+
+ return (r < 0
+ ? integer_minus_one_node
+ : (r > 0 ? integer_one_node : integer_zero_node));
+ }
+
+ /* 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)
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree ind1 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg1))));
+ tree ind2 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg2))));
+ return fold (build (MINUS_EXPR, integer_type_node, ind1, ind2));
+ }
+
+ return 0;
+}
+
+/* Simplify a call to the strcmp builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+static tree
+simplify_builtin_strcmp (tree arglist)
+{
+ 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 both arguments are equal (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return integer_zero_node;
+
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ if (p1 && p2)
+ {
+ const int i = strcmp (p1, p2);
+ return (i < 0
+ ? integer_minus_one_node
+ : (i > 0 ? integer_one_node : integer_zero_node));
+ }
+
+ /* If either arg is "", return an expression corresponding to
+ (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
+ if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree ind1 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg1))));
+ tree ind2 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg2))));
+ return fold (build (MINUS_EXPR, integer_type_node, ind1, ind2));
+ }
+
+ return 0;
+}
+
+/* Simplify a call to the strncmp builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
+
+static tree
+simplify_builtin_strncmp (tree arglist)
+{
+ tree arg1, arg2, arg3;
+ 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));
+ arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* If the len parameter is zero, return zero. */
+ if (integer_zerop (arg3))
+ {
+ /* Evaluate and ignore arg1 and arg2 in case they have
+ side-effects. */
+ return build (COMPOUND_EXPR, integer_type_node, arg1,
+ build (COMPOUND_EXPR, integer_type_node,
+ arg2, integer_zero_node));
+ }