builtins.c (fold_builtin_strstr): Removed.

* builtins.c (fold_builtin_strstr): Removed.
	(fold_builtin_2): Don't call fold_builtin_strstr.
	* gimple-fold.c (gimple_fold_builtin_strchr): Check is_strrchr
	earlier in the strrchr (x, 0) -> strchr (x, 0) optimization.
	(gimple_fold_builtin_strstr): New function.
	(gimple_fold_builtin): Call it.
	* fold-const-call.c (fold_const_call): Handle CFN_BUILT_IN_STRSTR.

	* gcc.dg/builtin-strstr-1.c: New test.
	* g++.dg/cpp0x/constexpr-strstr.C: New test.

From-SVN: r243378
This commit is contained in:
Jakub Jelinek 2016-12-07 20:45:45 +01:00 committed by Jakub Jelinek
parent 77f1efdbe8
commit c89529306c
7 changed files with 136 additions and 72 deletions

View File

@ -1,5 +1,13 @@
2016-12-07 Jakub Jelinek <jakub@redhat.com>
* builtins.c (fold_builtin_strstr): Removed.
(fold_builtin_2): Don't call fold_builtin_strstr.
* gimple-fold.c (gimple_fold_builtin_strchr): Check is_strrchr
earlier in the strrchr (x, 0) -> strchr (x, 0) optimization.
(gimple_fold_builtin_strstr): New function.
(gimple_fold_builtin): Call it.
* fold-const-call.c (fold_const_call): Handle CFN_BUILT_IN_STRSTR.
PR c++/78692
* cgraph.c (cgraph_edge::redirect_call_stmt_to_callee): Set lhs
var to lhs of new_stmt right before noreturn handling rather than to

View File

@ -163,7 +163,6 @@ static tree fold_builtin_3 (location_t, tree, tree, tree, tree);
static tree fold_builtin_varargs (location_t, tree, tree*, int);
static tree fold_builtin_strpbrk (location_t, tree, tree, tree);
static tree fold_builtin_strstr (location_t, tree, tree, tree);
static tree fold_builtin_strspn (location_t, tree, tree);
static tree fold_builtin_strcspn (location_t, tree, tree);
@ -8303,9 +8302,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
CASE_FLT_FN (BUILT_IN_MODF):
return fold_builtin_modf (loc, arg0, arg1, type);
case BUILT_IN_STRSTR:
return fold_builtin_strstr (loc, arg0, arg1, type);
case BUILT_IN_STRSPN:
return fold_builtin_strspn (loc, arg0, arg1);
@ -8729,72 +8725,6 @@ readonly_data_expr (tree exp)
return false;
}
/* Simplify a call to the strstr 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_strstr (location_t loc, tree s1, tree s2, tree type)
{
if (!validate_arg (s1, POINTER_TYPE)
|| !validate_arg (s2, POINTER_TYPE))
return NULL_TREE;
else
{
tree fn;
const char *p1, *p2;
p2 = c_getstr (s2);
if (p2 == NULL)
return NULL_TREE;
p1 = c_getstr (s1);
if (p1 != NULL)
{
const char *r = strstr (p1, p2);
tree tem;
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);
}
/* The argument is const char *, and the result is char *, so we need
a type conversion here to avoid a warning. */
if (p2[0] == '\0')
return fold_convert_loc (loc, type, s1);
if (p2[1] != '\0')
return NULL_TREE;
fn = builtin_decl_implicit (BUILT_IN_STRCHR);
if (!fn)
return NULL_TREE;
/* New argument list transforming strstr(s1, s2) to
strchr(s1, s2[0]). */
return build_call_expr_loc (loc, fn, 2, s1,
build_int_cst (integer_type_node, p2[0]));
}
}
/* Simplify a call to the strpbrk builtin. S1 and S2 are the arguments
to the call, and TYPE is its return type.

View File

@ -1434,6 +1434,22 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
}
return NULL_TREE;
case CFN_BUILT_IN_STRSTR:
if ((p1 = c_getstr (arg1)))
{
if ((p0 = c_getstr (arg0)))
{
const char *r = strstr (p0, p1);
if (r == NULL)
return build_int_cst (type, 0);
return fold_convert (type,
fold_build_pointer_plus_hwi (arg0, r - p0));
}
if (*p1 == '\0')
return fold_convert (type, arg0);
}
return NULL_TREE;
default:
return fold_const_call_1 (fn, type, arg0, arg1);
}

View File

@ -1506,11 +1506,11 @@ gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
return false;
/* Transform strrchr (s, 0) to strchr (s, 0) when optimizing for size. */
if (optimize_function_for_size_p (cfun))
if (is_strrchr && optimize_function_for_size_p (cfun))
{
tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
if (is_strrchr && strchr_fn)
if (strchr_fn)
{
gimple *repl = gimple_build_call (strchr_fn, 2, str, c);
replace_call_with_call_and_fold (gsi, repl);
@ -1549,6 +1549,68 @@ gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
return true;
}
/* Fold function call to builtin strstr.
If both arguments are constant, evaluate and fold the result,
additionally fold strstr (x, "") into x and strstr (x, "c")
into strchr (x, 'c'). */
static bool
gimple_fold_builtin_strstr (gimple_stmt_iterator *gsi)
{
gimple *stmt = gsi_stmt (*gsi);
tree haystack = gimple_call_arg (stmt, 0);
tree needle = gimple_call_arg (stmt, 1);
const char *p, *q;
if (!gimple_call_lhs (stmt))
return false;
q = c_getstr (needle);
if (q == NULL)
return false;
if ((p = c_getstr (haystack)))
{
const char *r = strstr (p, q);
if (r == NULL)
{
replace_call_with_value (gsi, integer_zero_node);
return true;
}
tree len = build_int_cst (size_type_node, r - p);
gimple_seq stmts = NULL;
gimple *new_stmt
= gimple_build_assign (gimple_call_lhs (stmt), POINTER_PLUS_EXPR,
haystack, len);
gimple_seq_add_stmt_without_update (&stmts, new_stmt);
gsi_replace_with_seq_vops (gsi, stmts);
return true;
}
/* For strstr (x, "") return x. */
if (q[0] == '\0')
{
replace_call_with_value (gsi, haystack);
return true;
}
/* Transform strstr (x, "c") into strchr (x, 'c'). */
if (q[1] == '\0')
{
tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
if (strchr_fn)
{
tree c = build_int_cst (integer_type_node, q[0]);
gimple *repl = gimple_build_call (strchr_fn, 2, haystack, c);
replace_call_with_call_and_fold (gsi, repl);
return true;
}
}
return false;
}
/* Simplify a call to the strcat builtin. DST and SRC are the arguments
to the call.
@ -3236,6 +3298,8 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
case BUILT_IN_RINDEX:
case BUILT_IN_STRRCHR:
return gimple_fold_builtin_strchr (gsi, true);
case BUILT_IN_STRSTR:
return gimple_fold_builtin_strstr (gsi);
case BUILT_IN_STRCMP:
case BUILT_IN_STRCASECMP:
case BUILT_IN_STRNCMP:

View File

@ -1,5 +1,8 @@
2016-12-07 Jakub Jelinek <jakub@redhat.com>
* gcc.dg/builtin-strstr-1.c: New test.
* g++.dg/cpp0x/constexpr-strstr.C: New test.
PR c++/78692
* g++.dg/torture/pr78692.C: New test.

View File

@ -0,0 +1,12 @@
// { dg-do compile { target c++11 } }
constexpr const char *f1 (const char *p, const char *q) { return __builtin_strstr (p, q); }
constexpr const char a[] = "abcdefedcbaaaaab";
constexpr const char b[] = "fed";
constexpr const char c[] = "aaab";
static_assert (f1 ("abcde", "ee") == nullptr, "");
static_assert (f1 (a, b) == a + 5, "");
static_assert (f1 (a, c) == a + 12, "");
static_assert (f1 (a, "") == a, "");
static_assert (f1 (a, "aaaaaab") == nullptr, "");
static_assert (f1 (a, "aaa") == a + 10, "");

View File

@ -0,0 +1,31 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */
/* { dg-final { scan-tree-dump-not "link_error" "optimized" } } */
/* { dg-final { scan-tree-dump-not "__builtin_strstr" "optimized" } } */
/* { dg-final { scan-tree-dump-times "return p_\[0-9]*.D.;" 1 "optimized" } } */
/* { dg-final { scan-tree-dump-times "__builtin_strchr" 1 "optimized" } } */
extern void link_error (void);
void
foo (void)
{
const char *p = "abcdef";
const char *q = "def";
p++;
q++;
if (__builtin_strstr (p, q) != p + 3)
link_error ();
}
char *
bar (const char *p)
{
return __builtin_strstr (p, "");
}
char *
baz (const char *p)
{
return __builtin_strstr (p, "d");
}