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

Reply via email to