static bool validate_arg (const_tree, enum tree_code code);
static rtx expand_builtin_fabs (tree, rtx, rtx);
static rtx expand_builtin_signbit (tree, rtx);
-static tree fold_builtin_strchr (location_t, tree, tree, tree);
static tree fold_builtin_memchr (location_t, tree, tree, tree, tree);
static tree fold_builtin_memcmp (location_t, tree, tree, tree);
static tree fold_builtin_strcmp (location_t, tree, tree);
static tree fold_builtin_strpbrk (location_t, tree, tree, tree);
static tree fold_builtin_strstr (location_t, tree, tree, tree);
-static tree fold_builtin_strrchr (location_t, tree, tree, tree);
static tree fold_builtin_strspn (location_t, tree, tree);
static tree fold_builtin_strcspn (location_t, tree, tree);
case BUILT_IN_STRCSPN:
return fold_builtin_strcspn (loc, arg0, arg1);
- case BUILT_IN_STRCHR:
- case BUILT_IN_INDEX:
- return fold_builtin_strchr (loc, arg0, arg1, type);
-
- case BUILT_IN_STRRCHR:
- case BUILT_IN_RINDEX:
- return fold_builtin_strrchr (loc, arg0, arg1, type);
-
case BUILT_IN_STRCMP:
return fold_builtin_strcmp (loc, arg0, arg1);
}
}
-/* Simplify a call to the strchr builtin. S1 and S2 are the arguments to
- the call, and TYPE is its return type.
-
- Return NULL_TREE 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
-fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, INTEGER_TYPE))
- return NULL_TREE;
- else
- {
- const char *p1;
-
- if (TREE_CODE (s2) != INTEGER_CST)
- return NULL_TREE;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- char c;
- const char *r;
- tree tem;
-
- if (target_char_cast (s2, &c))
- return NULL_TREE;
-
- r = strchr (p1, c);
-
- if (r == NULL)
- return build_int_cst (TREE_TYPE (s1), 0);
-
- /* Return an offset into the constant string argument. */
- tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
- return fold_convert_loc (loc, type, tem);
- }
- return NULL_TREE;
- }
-}
-
-/* Simplify a call to the strrchr builtin. S1 and S2 are the arguments to
- the call, and TYPE is its return type.
-
- Return NULL_TREE 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
-fold_builtin_strrchr (location_t loc, tree s1, tree s2, tree type)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, INTEGER_TYPE))
- return NULL_TREE;
- else
- {
- tree fn;
- const char *p1;
-
- if (TREE_CODE (s2) != INTEGER_CST)
- return NULL_TREE;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- char c;
- const char *r;
- tree tem;
-
- if (target_char_cast (s2, &c))
- return NULL_TREE;
-
- r = strrchr (p1, c);
-
- if (r == NULL)
- return build_int_cst (TREE_TYPE (s1), 0);
-
- /* Return an offset into the constant string argument. */
- tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
- return fold_convert_loc (loc, type, tem);
- }
-
- if (! integer_zerop (s2))
- return NULL_TREE;
-
- fn = builtin_decl_implicit (BUILT_IN_STRCHR);
- if (!fn)
- return NULL_TREE;
-
- /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */
- return build_call_expr_loc (loc, fn, 2, s1, s2);
- }
-}
-
/* Simplify a call to the strpbrk builtin. S1 and S2 are the arguments
to the call, and TYPE is its return type.
#include "tree-cfg.h"
+/* Return true if T is a constant and the value cast to a target char
+ can be represented by a host char.
+ Store the casted char constant in *P if so. */
+
+static bool
+target_char_cst_p (tree t, char *p)
+{
+ if (!tree_fits_uhwi_p (t) || CHAR_TYPE_SIZE != HOST_BITS_PER_CHAR)
+ return false;
+
+ *p = (char)tree_to_uhwi (t);
+ return true;
+}
+
/* Return true when DECL can be referenced from current unit.
FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
We can get declarations that are not possible to reference for various
return true;
}
-/* Simplify strchr (str, 0) into str + strlen (str).
+/* Fold function call to builtin strchr or strrchr.
+ If both arguments are constant, evaluate and fold the result,
+ otherwise simplify str(r)chr (str, 0) into str + strlen (str).
In general strlen is significantly faster than strchr
due to being a simpler operation. */
static bool
-gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi)
+gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
{
gimple *stmt = gsi_stmt (*gsi);
tree str = gimple_call_arg (stmt, 0);
tree c = gimple_call_arg (stmt, 1);
location_t loc = gimple_location (stmt);
+ const char *p;
+ char ch;
- if (optimize_function_for_size_p (cfun))
+ if (!gimple_call_lhs (stmt))
return false;
- if (!integer_zerop (c) || !gimple_call_lhs (stmt))
+ if ((p = c_getstr (str)) && target_char_cst_p (c, &ch))
+ {
+ const char *p1 = is_strrchr ? strrchr (p, ch) : strchr (p, ch);
+
+ if (p1 == NULL)
+ {
+ replace_call_with_value (gsi, integer_zero_node);
+ return true;
+ }
+
+ tree len = build_int_cst (size_type_node, p1 - p);
+ gimple_seq stmts = NULL;
+ gimple *new_stmt = gimple_build_assign (gimple_call_lhs (stmt),
+ POINTER_PLUS_EXPR, str, len);
+ gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+ gsi_replace_with_seq_vops (gsi, stmts);
+ return true;
+ }
+
+ if (!integer_zerop (c))
return false;
+ /* Transform strrchr (s, 0) to strchr (s, 0) when optimizing for size. */
+ if (optimize_function_for_size_p (cfun))
+ {
+ tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+
+ if (is_strrchr && strchr_fn)
+ {
+ gimple *repl = gimple_build_call (strchr_fn, 2, str, c);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+
+ return false;
+ }
+
tree len;
tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
gimple_call_arg (stmt, 1));
case BUILT_IN_STRNCAT:
return gimple_fold_builtin_strncat (gsi);
+ case BUILT_IN_INDEX:
case BUILT_IN_STRCHR:
- if (gimple_fold_builtin_strchr (gsi))
- return true;
- /* Perform additional folding in builtin.c. */
- break;
+ return gimple_fold_builtin_strchr (gsi, false);
+ case BUILT_IN_RINDEX:
+ case BUILT_IN_STRRCHR:
+ return gimple_fold_builtin_strchr (gsi, true);
case BUILT_IN_FPUTS:
return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
gimple_call_arg (stmt, 1), false);