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:
parent
77f1efdbe8
commit
c89529306c
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
||||
|
12
gcc/testsuite/g++.dg/cpp0x/constexpr-strstr.C
Normal file
12
gcc/testsuite/g++.dg/cpp0x/constexpr-strstr.C
Normal 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, "");
|
31
gcc/testsuite/gcc.dg/builtin-strstr-1.c
Normal file
31
gcc/testsuite/gcc.dg/builtin-strstr-1.c
Normal 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");
|
||||
}
|
Loading…
Reference in New Issue
Block a user