Hi
For now, gcc can not fold code like:
const char a[5] = "123"
__builtin_memchr (a, '7', sizeof a)
It tries to avoid folding out of string length although length of a is 5.
This is a bit conservative, it's safe to folding memchr/bcmp/memcmp
builtins when constant string stores in array with some trailing nuls.
This patch folds these cases by exposing additional length of
trailing nuls in c_getstr().
Bootstrapped/regtested on x86_64-linux, ok for trunk?
Regards
JunMa
gcc/ChangeLog
2019-03-21 Jun Ma <ju...@linux.alibaba.com>
PR Tree-optimization/89772
* fold-const.c (c_getstr): Add new parameter to get length of
additional
trailing nuls after constant string.
* gimple-fold.c (gimple_fold_builtin_memchr): consider trailing nuls in
out-of-bound accesses checking.
* fold-const-call.c (fold_const_call): Likewise.
gcc/testsuite/ChangeLog
2019-03-21 Jun Ma <ju...@linux.alibaba.com>
PR Tree-optimization/89772
* gcc.dg/builtin-memchr-4.c: New test.
---
gcc/fold-const-call.c | 14 +++++++-------
gcc/fold-const.c | 14 +++++++-------
gcc/fold-const.h | 3 ++-
gcc/gimple-fold.c | 5 +++--
gcc/testsuite/gcc.dg/builtin-memchr-4.c | 30 ++++++++++++++++++++++++++++++
5 files changed, 49 insertions(+), 17 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/builtin-memchr-4.c
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 702c8b4..ea81f6a 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -1720,7 +1720,7 @@ fold_const_call (combined_fn fn, tree type, tree arg0,
tree arg1, tree arg2)
{
const char *p0, *p1;
char c;
- unsigned HOST_WIDE_INT s0, s1;
+ unsigned HOST_WIDE_INT s0, s1, s3, s4;
size_t s2 = 0;
switch (fn)
{
@@ -1756,10 +1756,10 @@ fold_const_call (combined_fn fn, tree type, tree arg0,
tree arg1, tree arg2)
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
- if ((p0 = c_getstr (arg0, &s0))
- && (p1 = c_getstr (arg1, &s1))
- && s2 <= s0
- && s2 <= s1)
+ if ((p0 = c_getstr (arg0, &s0, &s3))
+ && (p1 = c_getstr (arg1, &s1, &s4))
+ && s2 <= s0 + s3
+ && s2 <= s1 + s4)
return build_cmp_result (type, memcmp (p0, p1, s2));
return NULL_TREE;
@@ -1770,8 +1770,8 @@ fold_const_call (combined_fn fn, tree type, tree arg0,
tree arg1, tree arg2)
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
- if ((p0 = c_getstr (arg0, &s0))
- && s2 <= s0
+ if ((p0 = c_getstr (arg0, &s0, &s3))
+ && s2 <= s0 + s3
&& target_char_cst_p (arg1, &c))
{
const char *r = (const char *) memchr (p0, c, s2);
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index ec28b43..413f0f0 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -14607,10 +14607,13 @@ fold_build_pointer_plus_hwi_loc (location_t loc, tree
ptr, HOST_WIDE_INT off)
characters within it if SRC is a reference to a string plus some
constant offset). If STRLEN is non-null, store the number of bytes
in the string constant including the terminating NUL char. *STRLEN is
- typically strlen(P) + 1 in the absence of embedded NUL characters. */
+ typically strlen(P) + 1 in the absence of embedded NUL characters.
+ If TRAILINGNULSLEN is non-null, store the number of trailing NUL chars
+ after terminating NUL char of pointer P. */
const char *
-c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */)
+c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */,
+ unsigned HOST_WIDE_INT *trailingnulslen /* =NULL */)
{
tree offset_node;
tree mem_size;
@@ -14639,16 +14642,13 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /*
= NULL */)
literal is stored in. */
unsigned HOST_WIDE_INT string_length = TREE_STRING_LENGTH (src);
unsigned HOST_WIDE_INT string_size = tree_to_uhwi (mem_size);
-
- /* Ideally this would turn into a gcc_checking_assert over time. */
- if (string_length > string_size)
- string_length = string_size;
-
const char *string = TREE_STRING_POINTER (src);
/* Ideally this would turn into a gcc_checking_assert over time. */
if (string_length > string_size)
string_length = string_size;
+ if (trailingnulslen)
+ *trailingnulslen = string_size - string_length;
if (string_length == 0
|| offset >= string_size)
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index 049fee9..5073138 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -187,7 +187,8 @@ extern bool expr_not_equal_to (tree t, const wide_int &);
extern tree const_unop (enum tree_code, tree, tree);
extern tree const_binop (enum tree_code, tree, tree, tree);
extern bool negate_mathfn_p (combined_fn);
-extern const char *c_getstr (tree, unsigned HOST_WIDE_INT * = NULL);
+extern const char *c_getstr (tree, unsigned HOST_WIDE_INT * = NULL,
+ unsigned HOST_WIDE_INT * = NULL);
extern wide_int tree_nonzero_bits (const_tree);
/* Return OFF converted to a pointer offset type suitable as offset for
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 62d2e0a..096b950 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -2541,14 +2541,15 @@ gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
unsigned HOST_WIDE_INT length = tree_to_uhwi (len);
unsigned HOST_WIDE_INT string_length;
- const char *p1 = c_getstr (arg1, &string_length);
+ unsigned HOST_WIDE_INT trailingnuls_length;
+ const char *p1 = c_getstr (arg1, &string_length, &trailingnuls_length);
if (p1)
{
const char *r = (const char *)memchr (p1, c, MIN (length,
string_length));
if (r == NULL)
{
- if (length <= string_length)
+ if (length <= string_length + trailingnuls_length)
{
replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
return true;
diff --git a/gcc/testsuite/gcc.dg/builtin-memchr-4.c
b/gcc/testsuite/gcc.dg/builtin-memchr-4.c
new file mode 100644
index 0000000..c37df60
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-memchr-4.c
@@ -0,0 +1,30 @@
+/* PR tree-optimization/89772
+ Verify that memchr calls with a pointer to a constant character
+ are folded as expected.
+ { dg-do compile }
+ { dg-options "-O1 -Wall -fdump-tree-gimple" } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+
+extern void* memchr (const void*, int, size_t);
+extern int printf (const char*, ...);
+extern void abort (void);
+
+#define A(expr) \
+ ((expr) \
+ ? (void)0 \
+ : (printf ("assertion failed on line %i: %s\n", \
+ __LINE__, #expr), \
+ abort ()))
+
+const char a[8] = {'a',0,'b'};
+
+void test_memchr_cst_char (void)
+{
+ A (!memchr (a, 'c', 2));
+ A (!memchr (a, 'c', 5));
+ A (!memchr (a, 'c', sizeof a));
+}
+
+/* { dg-final { scan-tree-dump-not "abort" "gimple" } } */
--
1.8.3.1