+
+/* 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 = fold_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);
+
+ if (r == NULL)
+ return fold_convert (TREE_TYPE (s1), integer_zero_node);
+
+ /* Return an offset into the constant string argument. */
+ return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, fold_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 fold_convert (TREE_TYPE (s1), integer_zero_node);
+
+ /* Return an offset into the constant string argument. */
+ return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, fold_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 fold_convert (TREE_TYPE (s1), integer_zero_node);
+
+ /* Return an offset into the constant string argument. */
+ return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, fold_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 fold_convert (TREE_TYPE (s1), integer_zero_node);
+
+ /* Return an offset into the constant string argument. */
+ return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, fold_convert (TREE_TYPE (s1),
+ ssize_int (r - p1))));
+ }
+
+ if (p2[0] == '\0')
+ /* strpbrk(x, "") == NULL.
+ Evaluate and ignore s1 in case it had side-effects. */
+ return omit_one_operand (TREE_TYPE (s1), integer_zero_node, s1);
+
+ 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, src, dst;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+
+ fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ if (!fn)
+ return 0;
+
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ dst = TREE_VALUE (arglist);
+
+ if (!len)
+ {
+ len = c_strlen (src, 1);
+ if (!len || TREE_SIDE_EFFECTS (len))
+ return 0;
+ }
+
+ len = size_binop (PLUS_EXPR, len, ssize_int (1));
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dst, arglist);
+ 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 omit_one_operand (TREE_TYPE (TREE_VALUE (arglist)),
+ TREE_VALUE (arglist),
+ TREE_VALUE (TREE_CHAIN (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 omit_two_operands (integer_type_node, integer_zero_node,
+ arg1, arg2);
+
+ 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 (build2 (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 (build2 (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 omit_two_operands (integer_type_node, integer_zero_node,
+ arg1, arg2);
+
+ /* If arg1 and arg2 are equal (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ /* Evaluate and ignore arg3 in case it has side-effects. */
+ return omit_one_operand (integer_type_node, integer_zero_node, arg3);
+
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ /* If all arguments are constant, evaluate at compile-time. */
+ if (host_integerp (arg3, 1) && p1 && p2)
+ {
+ const int r = strncmp (p1, p2, tree_low_cst (arg3, 1));
+ return (r < 0
+ ? integer_minus_one_node
+ : (r > 0 ? integer_one_node : integer_zero_node));
+ }
+
+ /* If len == 1 or (either string parameter is "" and (len >= 1)),
+ return (*(const u_char*)arg1 - *(const u_char*)arg2). */
+ if (host_integerp (arg3, 1)
+ && (tree_low_cst (arg3, 1) == 1
+ || (tree_low_cst (arg3, 1) > 1
+ && ((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 (build2 (MINUS_EXPR, integer_type_node, ind1, ind2));
+ }
+
+ return 0;
+}
+
+/* Simplify a call to the strcat 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_strcat (tree arglist)
+{
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree dst = TREE_VALUE (arglist),
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ const char *p = c_getstr (src);
+
+ /* If the string length is zero, return the dst parameter. */
+ if (p && *p == '\0')
+ return dst;
+
+ return 0;
+ }
+}
+
+/* Simplify a call to the strncat 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_strncat (tree arglist)
+{
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree dst = TREE_VALUE (arglist);
+ tree src = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ const char *p = c_getstr (src);
+
+ /* If the requested length is zero, or the src parameter string
+ length is zero, return the dst parameter. */
+ if (integer_zerop (len) || (p && *p == '\0'))
+ return omit_two_operands (TREE_TYPE (dst), dst, src, len);
+
+ /* If the requested len is greater than or equal to the string
+ length, call strcat. */
+ if (TREE_CODE (len) == INTEGER_CST && p
+ && compare_tree_int (len, strlen (p)) >= 0)
+ {
+ tree newarglist
+ = tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src));
+ tree fn = implicit_built_in_decls[BUILT_IN_STRCAT];
+
+ /* If the replacement _DECL isn't initialized, don't do the
+ transformation. */
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, newarglist);
+ }
+ return 0;
+ }
+}
+
+/* Simplify a call to the strspn 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_strspn (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));
+ const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
+
+ /* If both arguments are constants, evaluate at compile-time. */
+ if (p1 && p2)
+ {
+ const size_t r = strspn (p1, p2);
+ return size_int (r);
+ }
+
+ /* If either argument is "", return 0. */
+ if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+ /* Evaluate and ignore both arguments in case either one has
+ side-effects. */
+ return omit_two_operands (integer_type_node, integer_zero_node,
+ s1, s2);
+ return 0;
+ }
+}
+
+/* Simplify a call to the strcspn 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_strcspn (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));
+ const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
+
+ /* If both arguments are constants, evaluate at compile-time. */
+ if (p1 && p2)
+ {
+ const size_t r = strcspn (p1, p2);
+ return size_int (r);
+ }
+
+ /* If the first argument is "", return 0. */
+ if (p1 && *p1 == '\0')
+ {
+ /* Evaluate and ignore argument s2 in case it has
+ side-effects. */
+ return omit_one_operand (integer_type_node,
+ integer_zero_node, s2);
+ }
+
+ /* If the second argument is "", return __builtin_strlen(s1). */
+ if (p2 && *p2 == '\0')
+ {
+ tree newarglist = build_tree_list (NULL_TREE, s1),
+ fn = implicit_built_in_decls[BUILT_IN_STRLEN];
+
+ /* If the replacement _DECL isn't initialized, don't do the
+ transformation. */
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, newarglist);
+ }
+ return 0;
+ }
+}
+
+/* Simplify a call to the fputs 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.
+
+ If KNOWN_LEN is non-NULL, it represents the known length of the string.
+ This is determined by SSA-CCP in cases where the string itself is not
+ known to be constant but its length is always the same constant. */
+
+tree
+simplify_builtin_fputs (tree arglist, int ignore, int unlocked, tree known_len)
+{
+ tree len, fn;
+ tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FPUTC];
+ tree fn_fwrite = unlocked ? implicit_built_in_decls[BUILT_IN_FWRITE_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FWRITE];
+
+ /* If the return value is used, or the replacement _DECL isn't
+ initialized, don't do the transformation. */
+ if (!ignore || !fn_fputc || !fn_fwrite)
+ return 0;
+
+ /* Verify the arguments in the original call. */
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+
+ len = (known_len) ? known_len : c_strlen (TREE_VALUE (arglist), 0);
+
+ /* Get the length of the string passed to fputs. If the length
+ can't be determined, punt. */
+ if (!len
+ || TREE_CODE (len) != INTEGER_CST)
+ return 0;
+
+ switch (compare_tree_int (len, 1))
+ {
+ case -1: /* length is 0, delete the call entirely . */
+ return omit_one_operand (integer_type_node, integer_zero_node,
+ TREE_VALUE (TREE_CHAIN (arglist)));
+
+ case 0: /* length is 1, call fputc. */
+ {
+ const char *p = c_getstr (TREE_VALUE (arglist));
+
+ if (p != NULL)
+ {
+ /* New argument list transforming fputs(string, stream) to
+ fputc(string[0], stream). */
+ arglist =
+ build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
+ arglist =
+ tree_cons (NULL_TREE, build_int_2 (p[0], 0), arglist);
+ fn = fn_fputc;
+ break;
+ }
+ }
+ /* FALLTHROUGH */
+ case 1: /* length is greater than 1, call fwrite. */
+ {
+ tree string_arg;
+
+ /* If optimizing for size keep fputs. */
+ if (optimize_size)
+ return 0;
+ string_arg = TREE_VALUE (arglist);
+ /* New argument list transforming fputs(string, stream) to
+ fwrite(string, 1, len, stream). */
+ arglist = build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
+ arglist = tree_cons (NULL_TREE, len, arglist);
+ arglist = tree_cons (NULL_TREE, size_one_node, arglist);
+ arglist = tree_cons (NULL_TREE, string_arg, arglist);
+ fn = fn_fwrite;
+ break;
+ }
+ default:
+ abort ();
+ }
+
+ return build_function_call_expr (fn, arglist);
+}
+
+static void
+simplify_builtin_va_start (tree arglist)
+{
+ tree chain = TREE_CHAIN (arglist);
+
+ if (TREE_CHAIN (chain))
+ error ("too many arguments to function `va_start'");
+
+ simplify_builtin_next_arg (chain);
+}
+
+static void
+simplify_builtin_next_arg (tree arglist)
+{
+ tree fntype = TREE_TYPE (current_function_decl);
+
+ if (TYPE_ARG_TYPES (fntype) == 0
+ || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+ == void_type_node))
+ error ("`va_start' used in function with fixed args");
+ else if (arglist)
+ {
+ tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
+ tree arg = TREE_VALUE (arglist);
+
+ /* Strip off all nops for the sake of the comparison. This
+ is not quite the same as STRIP_NOPS. It does more.
+ We must also strip off INDIRECT_EXPR for C++ reference
+ parameters. */
+ while (TREE_CODE (arg) == NOP_EXPR
+ || TREE_CODE (arg) == CONVERT_EXPR
+ || TREE_CODE (arg) == NON_LVALUE_EXPR
+ || TREE_CODE (arg) == INDIRECT_REF)
+ arg = TREE_OPERAND (arg, 0);
+ if (arg != last_parm)
+ warning ("second parameter of `va_start' not last named argument");
+ TREE_VALUE (arglist) = arg;
+ }
+ else
+ /* Evidently an out of date version of <stdarg.h>; can't validate
+ va_start's second argument, but can still work as intended. */
+ warning ("`__builtin_next_arg' called without an argument");
+}
+
+
+/* Simplify a call to the sprintf builtin.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree. If IGNORED is true, it means that
+ the caller does not use the returned value of the function. */
+
+static tree
+simplify_builtin_sprintf (tree arglist, int ignored)
+{
+ tree call, retval, dest, fmt;
+ const char *fmt_str = NULL;
+
+ /* Verify the required arguments in the original call. We deal with two
+ types of sprintf() calls: 'sprintf (str, fmt)' and
+ 'sprintf (dest, "%s", orig)'. */
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+ && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE,
+ VOID_TYPE))
+ return NULL_TREE;
+
+ /* Get the destination string and the format specifier. */
+ dest = TREE_VALUE (arglist);
+ fmt = TREE_VALUE (TREE_CHAIN (arglist));
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return NULL_TREE;
+
+ call = NULL_TREE;
+ retval = NULL_TREE;
+
+ /* If the format doesn't contain % args or %%, use strcpy. */
+ if (strchr (fmt_str, '%') == NULL)
+ {
+ tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+
+ if (!fn)
+ return NULL_TREE;
+
+ /* Convert sprintf (str, fmt) into strcpy (str, fmt) when
+ 'format' is known to contain no % formats. */
+ arglist = build_tree_list (NULL_TREE, fmt);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+ call = build_function_call_expr (fn, arglist);
+ if (!ignored)
+ retval = build_int_2 (strlen (fmt_str), 0);
+ }
+
+ /* If the format is "%s", use strcpy if the result isn't used. */
+ else if (fmt_str && strcmp (fmt_str, "%s") == 0)
+ {
+ tree fn, orig;
+ fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+
+ if (!fn)
+ return NULL_TREE;
+
+ /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2). */
+ orig = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ arglist = build_tree_list (NULL_TREE, orig);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+ if (!ignored)
+ {
+ retval = c_strlen (orig, 1);
+ if (!retval || TREE_CODE (retval) != INTEGER_CST)
+ return NULL_TREE;
+ }
+ call = build_function_call_expr (fn, arglist);
+ }
+
+ if (call && retval)
+ {
+ retval = convert
+ (TREE_TYPE (TREE_TYPE (implicit_built_in_decls[BUILT_IN_SPRINTF])),
+ retval);
+ return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval);
+ }
+ else
+ return call;
+}