public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 1/3] Fold BUILT_IN_STRNCASECMP
  2016-08-17  6:55 [PATCH 0/3] Better folding of 2 string builtin-ins marxin
  2016-08-17  6:55 ` [PATCH 2/3] Smarter folding of __builtin_memchr marxin
  2016-08-17  6:55 ` [PATCH 3/3] Test folding of strn{case}cmp and memchr marxin
@ 2016-08-17  6:55 ` marxin
  2016-08-17  7:10   ` Jakub Jelinek
  2016-10-07  8:39   ` [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2) Martin Liška
  2016-10-12 13:48 ` [RFC] Possible folding opportunities for string built-ins Martin Liška
  3 siblings, 2 replies; 45+ messages in thread
From: marxin @ 2016-08-17  6:55 UTC (permalink / raw)
  To: gcc-patches

gcc/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_strncmp): Rename to
	fold_builtin_strncmp_strncasecmp and support also
	strncasecmp.
	(fold_builtin_3): Handle BUILT_IN_STRNCASECMP.
---
 gcc/builtins.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 03a0dc8..8f1c752 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -150,7 +150,8 @@ 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_strncmp (location_t, tree, tree, tree);
+static tree fold_builtin_strncmp_strncasecmp (location_t, tree, tree, tree,
+					      bool);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
 static tree fold_builtin_isdigit (location_t, tree);
@@ -7383,11 +7384,13 @@ fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
   return NULL_TREE;
 }
 
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
-   Return NULL_TREE if no simplification can be made.  */
+/* Fold function call to builtin strncmp (or strncasecmp) with arguments ARG1,
+   ARG2, and LEN.  Return NULL_TREE if no simplification can be made.
+   IS_STRNCASECMP is true for strncasecmp, false otherwise.  */
 
 static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
+fold_builtin_strncmp_strncasecmp (location_t loc, tree arg1, tree arg2,
+				  tree len, bool is_strncasecmp)
 {
   if (!validate_arg (arg1, POINTER_TYPE)
       || !validate_arg (arg2, POINTER_TYPE)
@@ -7442,7 +7445,8 @@ fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
 
   /* If len parameter is one, return an expression corresponding to
      (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
+  if (is_strncasecmp
+      && tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
     {
       tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
       tree cst_uchar_ptr_node
@@ -8483,7 +8487,10 @@ fold_builtin_3 (location_t loc, tree fndecl,
     break;
 
     case BUILT_IN_STRNCMP:
-      return fold_builtin_strncmp (loc, arg0, arg1, arg2);
+      return fold_builtin_strncmp_strncasecmp (loc, arg0, arg1, arg2, false);
+
+    case BUILT_IN_STRNCASECMP:
+      return fold_builtin_strncmp_strncasecmp (loc, arg0, arg1, arg2, true);
 
     case BUILT_IN_MEMCHR:
       return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 2/3] Smarter folding of __builtin_memchr
  2016-08-17  6:55 [PATCH 0/3] Better folding of 2 string builtin-ins marxin
@ 2016-08-17  6:55 ` marxin
  2016-08-17  8:41   ` Richard Biener
  2016-08-17  6:55 ` [PATCH 3/3] Test folding of strn{case}cmp and memchr marxin
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 45+ messages in thread
From: marxin @ 2016-08-17  6:55 UTC (permalink / raw)
  To: gcc-patches

gcc/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_memchr): Support following
	transformations:
	memchr (x, y, 0) -> NULL
	memchr ("known_string", 'n', 5) -> &"known_string" + 1
	memchr ("known_string", 'n', 1) -> NULL
---
 gcc/builtins.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 8f1c752..ac251f8 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -7267,8 +7267,12 @@ fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
 	  || !tree_fits_uhwi_p (len))
 	return NULL_TREE;
 
+      /* If the LEN parameter is zero, return zero.  */
+      if (integer_zerop (len))
+	return build_int_cst (TREE_TYPE (arg1), 0);
+
       p1 = c_getstr (arg1);
-      if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
+      if (p1)
 	{
 	  char c;
 	  const char *r;
@@ -7281,9 +7285,17 @@ fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
 
 	  if (r == NULL)
 	    return build_int_cst (TREE_TYPE (arg1), 0);
-
-	  tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
-	  return fold_convert_loc (loc, type, tem);
+	  else
+	    {
+	      size_t offset = r - p1;
+	      if (compare_tree_int (len, offset) <= 0)
+		return build_int_cst (TREE_TYPE (arg1), 0);
+	      else
+		{
+		  tem = fold_build_pointer_plus_hwi_loc (loc, arg1, offset);
+		  return fold_convert_loc (loc, type, tem);
+		}
+	    }
 	}
       return NULL_TREE;
     }
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 0/3] Better folding of 2 string builtin-ins
@ 2016-08-17  6:55 marxin
  2016-08-17  6:55 ` [PATCH 2/3] Smarter folding of __builtin_memchr marxin
                   ` (3 more replies)
  0 siblings, 4 replies; 45+ messages in thread
From: marxin @ 2016-08-17  6:55 UTC (permalink / raw)
  To: gcc-patches

Hi.

During playing with -fprofile-values I've noticed that some builtins
are not folded ideally. Fixing in attached mini patch series.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

marxin (3):
  Fold BUILT_IN_STRNCASECMP
  Smarter folding of __builtin_memchr
  Test folding of strn{case}cmp and memchr

 gcc/builtins.c                                   | 39 ++++++++++++----
 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c | 59 ++++++++++++++++++++++++
 2 files changed, 88 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c

-- 
2.9.2

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 3/3] Test folding of strn{case}cmp and memchr
  2016-08-17  6:55 [PATCH 0/3] Better folding of 2 string builtin-ins marxin
  2016-08-17  6:55 ` [PATCH 2/3] Smarter folding of __builtin_memchr marxin
@ 2016-08-17  6:55 ` marxin
  2016-08-17  7:52   ` Martin Liška
  2016-08-17  6:55 ` [PATCH 1/3] Fold BUILT_IN_STRNCASECMP marxin
  2016-10-12 13:48 ` [RFC] Possible folding opportunities for string built-ins Martin Liška
  3 siblings, 1 reply; 45+ messages in thread
From: marxin @ 2016-08-17  6:55 UTC (permalink / raw)
  To: gcc-patches

gcc/testsuite/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/tree-ssa/builtins-folding.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c | 59 ++++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c
new file mode 100644
index 0000000..df63681
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+char *buffer1;
+
+void
+main_test (void)
+{
+  const char* const foo1 = "hello world";
+
+  /* MEMCHR.  */
+  if (__builtin_memchr (foo1, 'x', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'x', 100))
+    __builtin_abort ();
+  if (__builtin_memchr (buffer1, 'x', 0) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'o', 11) != foo1 + 4)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'w', 2))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1 + 5, 'o', 6) != foo1 + 7)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 11) != foo1 + 10)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 10))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 12) != foo1 + 11)
+    __builtin_abort ();
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("ab", "ba", 1) >= 0)
+    __builtin_abort ();
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncasecmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_abort" "optimized" } } */
-- 
2.9.2

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 1/3] Fold BUILT_IN_STRNCASECMP
  2016-08-17  6:55 ` [PATCH 1/3] Fold BUILT_IN_STRNCASECMP marxin
@ 2016-08-17  7:10   ` Jakub Jelinek
  2016-08-17  7:52     ` Martin Liška
  2016-10-07  8:39   ` [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2) Martin Liška
  1 sibling, 1 reply; 45+ messages in thread
From: Jakub Jelinek @ 2016-08-17  7:10 UTC (permalink / raw)
  To: marxin; +Cc: gcc-patches

On Tue, Aug 16, 2016 at 03:10:13PM +0200, marxin wrote:
> 2016-08-16  Martin Liska  <mliska@suse.cz>
> 
> 	* builtins.c (fold_builtin_strncmp): Rename to
> 	fold_builtin_strncmp_strncasecmp and support also
> 	strncasecmp.
> 	(fold_builtin_3): Handle BUILT_IN_STRNCASECMP.
> ---
> -/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
> -   Return NULL_TREE if no simplification can be made.  */
> +/* Fold function call to builtin strncmp (or strncasecmp) with arguments ARG1,
> +   ARG2, and LEN.  Return NULL_TREE if no simplification can be made.
> +   IS_STRNCASECMP is true for strncasecmp, false otherwise.  */
>  
>  static tree
> -fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
> +fold_builtin_strncmp_strncasecmp (location_t loc, tree arg1, tree arg2,
> +				  tree len, bool is_strncasecmp)
>  {
>    if (!validate_arg (arg1, POINTER_TYPE)
>        || !validate_arg (arg2, POINTER_TYPE)
> @@ -7442,7 +7445,8 @@ fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
>  
>    /* If len parameter is one, return an expression corresponding to
>       (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
> -  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
> +  if (is_strncasecmp
> +      && tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)

Did you really mean to use this block for strncasecmp only (rather than for
strncmp only, i.e. !is_strncasecmp)?

Also, while you are changing this, I'd replace
  tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1
with integer_onep (len).

	Jakub

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 3/3] Test folding of strn{case}cmp and memchr
  2016-08-17  6:55 ` [PATCH 3/3] Test folding of strn{case}cmp and memchr marxin
@ 2016-08-17  7:52   ` Martin Liška
  2016-10-07  8:42     ` [PATCH 3/3] Test folding of str{n}{case}cmp and memchr (version 2) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-08-17  7:52 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 12 bytes --]

v2.

Martin

[-- Attachment #2: 0003-Test-folding-of-strn-case-cmp-and-memchr-v2.patch --]
[-- Type: text/x-patch, Size: 3304 bytes --]

From 42ea652d00fec821514d34b2af81f2a9e11b248c Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 16 Aug 2016 15:56:01 +0200
Subject: [PATCH 3/3] Test folding of strn{case}cmp and memchr

gcc/testsuite/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/tree-ssa/builtins-folding.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c | 74 ++++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c
new file mode 100644
index 0000000..48a86e3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c
@@ -0,0 +1,74 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+char *buffer1;
+char *buffer2;
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (1000);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (1000);
+  __builtin_strcpy (buffer2, foo1);
+
+  /* MEMCHR.  */
+  if (__builtin_memchr (foo1, 'x', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'x', 100))
+    __builtin_abort ();
+  if (__builtin_memchr (buffer1, 'x', 0) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'o', 11) != foo1 + 4)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'w', 2))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1 + 5, 'o', 6) != foo1 + 7)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 11) != foo1 + 10)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 10))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 12) != foo1 + 11)
+    __builtin_abort ();
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("ab", "ba", 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (buffer1, buffer2, 1) != 0)
+    __builtin_abort (); /* not folded away */
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("ab", "ba", 1) >= 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 1) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 100) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncasecmp" 3 "optimized" } } */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 1/3] Fold BUILT_IN_STRNCASECMP
  2016-08-17  7:10   ` Jakub Jelinek
@ 2016-08-17  7:52     ` Martin Liška
  0 siblings, 0 replies; 45+ messages in thread
From: Martin Liška @ 2016-08-17  7:52 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 471 bytes --]

On 08/17/2016 09:10 AM, Jakub Jelinek wrote:
> Did you really mean to use this block for strncasecmp only (rather than for
> strncmp only, i.e. !is_strncasecmp)?

Sure, that was a typo. Unfortunately I had a test case with strings that was
eaten by fold_const_call. I enhanced test coverage for that.

> 
> Also, while you are changing this, I'd replace
>   tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1
> with integer_onep (len).

Fixed in v2.

Martin

> 
> 	Jakub


[-- Attachment #2: 0001-Fold-BUILT_IN_STRNCASECMP-v2.patch --]
[-- Type: text/x-patch, Size: 2894 bytes --]

From f1c30311d2f9274bd213fa703ad51da8151a1b1b Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 16 Aug 2016 15:10:13 +0200
Subject: [PATCH 1/3] Fold BUILT_IN_STRNCASECMP

gcc/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_strncmp): Rename to
	fold_builtin_strncmp_strncasecmp and support also
	strncasecmp.
	(fold_builtin_3): Handle BUILT_IN_STRNCASECMP.
---
 gcc/builtins.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 03a0dc8..3926049 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -150,7 +150,8 @@ 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_strncmp (location_t, tree, tree, tree);
+static tree fold_builtin_strncmp_strncasecmp (location_t, tree, tree, tree,
+					      bool);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
 static tree fold_builtin_isdigit (location_t, tree);
@@ -7383,11 +7384,13 @@ fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
   return NULL_TREE;
 }
 
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
-   Return NULL_TREE if no simplification can be made.  */
+/* Fold function call to builtin strncmp (or strncasecmp) with arguments ARG1,
+   ARG2, and LEN.  Return NULL_TREE if no simplification can be made.
+   IS_STRNCASECMP is true for strncasecmp, false otherwise.  */
 
 static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
+fold_builtin_strncmp_strncasecmp (location_t loc, tree arg1, tree arg2,
+				  tree len, bool is_strncasecmp)
 {
   if (!validate_arg (arg1, POINTER_TYPE)
       || !validate_arg (arg2, POINTER_TYPE)
@@ -7442,7 +7445,7 @@ fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
 
   /* If len parameter is one, return an expression corresponding to
      (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
+  if (!is_strncasecmp && integer_onep (len))
     {
       tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
       tree cst_uchar_ptr_node
@@ -8483,7 +8486,10 @@ fold_builtin_3 (location_t loc, tree fndecl,
     break;
 
     case BUILT_IN_STRNCMP:
-      return fold_builtin_strncmp (loc, arg0, arg1, arg2);
+      return fold_builtin_strncmp_strncasecmp (loc, arg0, arg1, arg2, false);
+
+    case BUILT_IN_STRNCASECMP:
+      return fold_builtin_strncmp_strncasecmp (loc, arg0, arg1, arg2, true);
 
     case BUILT_IN_MEMCHR:
       return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 2/3] Smarter folding of __builtin_memchr
  2016-08-17  6:55 ` [PATCH 2/3] Smarter folding of __builtin_memchr marxin
@ 2016-08-17  8:41   ` Richard Biener
  2016-10-07  8:41     ` [PATCH 2/3] Fold __builtin_memchr (version 2) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-08-17  8:41 UTC (permalink / raw)
  To: marxin; +Cc: GCC Patches

On Tue, Aug 16, 2016 at 3:14 PM, marxin <mliska@suse.cz> wrote:
> gcc/ChangeLog:
>
> 2016-08-16  Martin Liska  <mliska@suse.cz>
>
>         * builtins.c (fold_builtin_memchr): Support following
>         transformations:
>         memchr (x, y, 0) -> NULL
>         memchr ("known_string", 'n', 5) -> &"known_string" + 1
>         memchr ("known_string", 'n', 1) -> NULL

Aww.  Can I convince you to move this and related foldings to gimple-fold.c?

Richard.

> ---
>  gcc/builtins.c | 20 ++++++++++++++++----
>  1 file changed, 16 insertions(+), 4 deletions(-)
>
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index 8f1c752..ac251f8 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -7267,8 +7267,12 @@ fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
>           || !tree_fits_uhwi_p (len))
>         return NULL_TREE;
>
> +      /* If the LEN parameter is zero, return zero.  */
> +      if (integer_zerop (len))
> +       return build_int_cst (TREE_TYPE (arg1), 0);
> +
>        p1 = c_getstr (arg1);
> -      if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
> +      if (p1)
>         {
>           char c;
>           const char *r;
> @@ -7281,9 +7285,17 @@ fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
>
>           if (r == NULL)
>             return build_int_cst (TREE_TYPE (arg1), 0);
> -
> -         tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
> -         return fold_convert_loc (loc, type, tem);
> +         else

the else seems superfluous and just adds to indentation.

> +           {
> +             size_t offset = r - p1;
> +             if (compare_tree_int (len, offset) <= 0)
> +               return build_int_cst (TREE_TYPE (arg1), 0);
> +             else
> +               {
> +                 tem = fold_build_pointer_plus_hwi_loc (loc, arg1, offset);
> +                 return fold_convert_loc (loc, type, tem);
> +               }
> +           }
>         }
>        return NULL_TREE;
>      }
> --
> 2.9.2
>
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2)
  2016-08-17  6:55 ` [PATCH 1/3] Fold BUILT_IN_STRNCASECMP marxin
  2016-08-17  7:10   ` Jakub Jelinek
@ 2016-10-07  8:39   ` Martin Liška
  2016-10-07 10:50     ` Richard Biener
  1 sibling, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-07  8:39 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 201 bytes --]

I'm resending the patch, where I implemented all builtins mentions in subject
in gimp-fold.c.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0001-Fold-__builtin_str-n-case-cmp-functions-v3.patch --]
[-- Type: text/x-patch, Size: 13371 bytes --]

From 8a5f823348c132523b40c531b56a1a29dac32097 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Wed, 5 Oct 2016 13:18:35 +0200
Subject: [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions

gcc/ChangeLog:

2016-10-06  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_strcmp): Remove function.
	(fold_builtin_strncmp): Likewise.
	(fold_builtin_2): Do not call fold_builtin_strcmp.
	(fold_builtin_3): Do not call fold_builtin_strncmp.
	* fold-const-call.c: Make build_cmp_result global fn.
	* fold-const-call.h: Likewise.
	* gimple-fold.c (gimple_fold_builtin_string_compare): New
	function.
	(gimple_fold_builtin): Call the function.
---
 gcc/builtins.c        | 138 -----------------------------------------------
 gcc/fold-const-call.c |   2 +-
 gcc/fold-const-call.h |   1 +
 gcc/gimple-fold.c     | 147 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 148 insertions(+), 140 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index facecd3..b03d53c 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -150,8 +150,6 @@ static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_signbit (tree, rtx);
 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_strncmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
 static tree fold_builtin_isdigit (location_t, tree);
@@ -7335,136 +7333,6 @@ fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
   return NULL_TREE;
 }
 
-/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE))
-    return NULL_TREE;
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return integer_zero_node;
-
-  /* If the second arg is "", return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp
-	= fold_convert_loc (loc, integer_type_node,
-			    build1 (INDIRECT_REF, cst_uchar_node,
-				    fold_convert_loc (loc,
-						      cst_uchar_ptr_node,
-						      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* If the LEN parameter is zero, return zero.  */
-  if (integer_zerop (len))
-    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
-			      arg1, arg2);
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
-
-  /* If the second arg is "", and the length is greater than zero,
-     return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", and the length is greater than zero,
-     return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  /* If len parameter is one, return an expression corresponding to
-     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree ind1 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg1)));
-      tree ind2 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold a call to builtin isascii with argument ARG.  */
 
 static tree
@@ -8393,9 +8261,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     case BUILT_IN_STRCSPN:
       return fold_builtin_strcspn (loc, arg0, arg1);
 
-    case BUILT_IN_STRCMP:
-      return fold_builtin_strcmp (loc, arg0, arg1);
-
     case BUILT_IN_STRPBRK:
       return fold_builtin_strpbrk (loc, arg0, arg1, type);
 
@@ -8477,9 +8342,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_STRNCMP:
-      return fold_builtin_strncmp (loc, arg0, arg1, arg2);
-
     case BUILT_IN_MEMCHR:
       return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
 
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 2bbc887..18f1182 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -69,7 +69,7 @@ host_size_t_cst_p (tree t, size_t *size_out)
    "equal" and > 0 means "more".  Canonicalize it to -1, 0 or 1 and
    return it in type TYPE.  */
 
-static inline tree
+tree
 build_cmp_result (tree type, int res)
 {
   return build_int_cst (type, res < 0 ? -1 : res > 0 ? 1 : 0);
diff --git a/gcc/fold-const-call.h b/gcc/fold-const-call.h
index 324ecbf..7f61f2e 100644
--- a/gcc/fold-const-call.h
+++ b/gcc/fold-const-call.h
@@ -24,5 +24,6 @@ tree fold_const_call (combined_fn, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree, tree);
 tree fold_fma (location_t, tree, tree, tree, tree);
+tree build_cmp_result (tree type, int res);
 
 #endif
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 59c4cb8..67a598c 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -55,7 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-low.h"
 #include "ipa-chkp.h"
 #include "tree-cfg.h"
-
+#include "fold-const-call.h"
 
 /* Return true if T is a constant and the value cast to a target char
    can be represented by a host char.
@@ -1784,6 +1784,143 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */
+
+static bool
+gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree callee = gimple_call_fndecl (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+
+  tree type = integer_type_node;
+  tree str1 = gimple_call_arg (stmt, 0);
+  tree str2 = gimple_call_arg (stmt, 1);
+  HOST_WIDE_INT length = -1;
+
+  /* Handle strncmp and strncasecmp functions.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_uhwi_p (len))
+	length = tree_to_uhwi (len);
+    }
+
+  const char *p1 = c_getstr (str1);
+  const char *p2 = c_getstr (str2);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (length == 0)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (str1, str2, 0))
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* For known strings, return an immediate value.  */
+  if (p1 && p2)
+    {
+      int r = 0;
+      bool known_result = false;
+
+      switch (fcode)
+	{
+	case BUILT_IN_STRNCASECMP:
+	  {
+	    r = strncmp (p1, p2, length);
+	    if (r == 0)
+	      known_result = true;
+	    break;;
+	  }
+	case BUILT_IN_STRCMP:
+	  {
+	    r = strcmp (p1, p2);
+	    known_result = true;
+	    break;
+	  }
+	case BUILT_IN_STRNCMP:
+	  {
+	    r = strncmp (p1, p2, length);
+	    known_result = true;
+	    break;
+	  }
+	default:
+	  break;
+	}
+
+      if (known_result)
+	{
+	  replace_call_with_value (gsi, build_cmp_result (type, r));
+	  return true;
+	}
+    }
+
+  location_t loc = gimple_location (stmt);
+  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+  tree cst_uchar_ptr_node
+    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
+
+  /* If the second arg is "", return *(const unsigned char*)arg1.  */
+  if (p2 && *p2 == '\0' && length != 0)
+    {
+      gimple_seq stmts = NULL;
+
+      tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str1,
+				   off0);
+      temp = gimple_build (&stmts, loc, NOP_EXPR, cst_uchar_node, temp);
+      gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+
+      replace_call_with_value (gsi, fold_convert_loc (loc, type, temp));
+      return true;
+    }
+
+  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
+  if (p1 && *p1 == '\0' && length != 0)
+    {
+      gimple_seq stmts = NULL;
+
+      tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str2,
+				   off0);
+      temp = gimple_build (&stmts, loc, NOP_EXPR, cst_uchar_node, temp);
+      temp = gimple_convert (&stmts, loc, type, temp);
+      gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+      replace_call_with_value (gsi, fold_build1_loc (loc, NEGATE_EXPR,
+						     type, temp));
+      return true;
+    }
+
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg2 - *(const unsigned char*)arg1).  */
+  if (fcode == BUILT_IN_STRNCMP && length == 1)
+    {
+      gimple_seq stmts = NULL;
+
+      tree temp1 = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str1,
+				    off0);
+      temp1 = gimple_build (&stmts, loc, NOP_EXPR, cst_uchar_node, temp1);
+      temp1 = gimple_convert (&stmts, loc, type, temp1);
+      tree temp2 = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str2,
+				    off0);
+      temp2 = gimple_build (&stmts, loc, NOP_EXPR, cst_uchar_node, temp2);
+      temp2 = gimple_convert (&stmts, loc, type, temp2);
+      tree temp = gimple_build (&stmts, loc, MINUS_EXPR, type, temp1, temp2);
+      gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+      replace_call_with_value (gsi, temp);
+      return true;
+    }
+
+  return false;
+}
+
+
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
    by the builtin will be ignored.  UNLOCKED is true is true if this
@@ -3005,6 +3142,14 @@ 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_STRCMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_STRCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_STRNCMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_STRNCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 2/3] Fold __builtin_memchr (version 2)
  2016-08-17  8:41   ` Richard Biener
@ 2016-10-07  8:41     ` Martin Liška
  2016-10-07 11:01       ` Richard Biener
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-07  8:41 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 173 bytes --]

Resending the patch, where I implemented folding in gimple-fold.c

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0002-Fold-__builtin_memchr-v3.patch --]
[-- Type: text/x-patch, Size: 6113 bytes --]

From d4c1257b092f245a52386e305391d1e46e2ef088 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 6 Oct 2016 17:52:45 +0200
Subject: [PATCH 2/3] Fold __builtin_memchr

gcc/ChangeLog:

2016-10-06  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_memchr): Remove.
	(fold_builtin_3): Do not call the function.
	* builtins.h: Declare target_char_cast.
	* gimple-fold.c (gimple_fold_builtin_memchr): New function.
	(gimple_fold_builtin): Call the function.
---
 gcc/builtins.c    | 48 +------------------------------------------
 gcc/builtins.h    |  1 +
 gcc/gimple-fold.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 47 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index b03d53c..97723dc 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -92,7 +92,6 @@ builtin_info_type builtin_info[(int)END_BUILTINS];
 bool force_folding_builtin_constant_p;
 
 static rtx c_readstr (const char *, machine_mode);
-static int target_char_cast (tree, char *);
 static rtx get_memory_rtx (tree, tree);
 static int apply_args_size (void);
 static int apply_result_size (void);
@@ -148,7 +147,6 @@ static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 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_memchr (location_t, tree, tree, tree, tree);
 static tree fold_builtin_memcmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
@@ -653,7 +651,7 @@ c_readstr (const char *str, machine_mode mode)
    host char type, return zero and put that value into variable pointed to by
    P.  */
 
-static int
+int
 target_char_cast (tree cst, char *p)
 {
   unsigned HOST_WIDE_INT val, hostval;
@@ -7246,47 +7244,6 @@ fold_builtin_sincos (location_t loc,
 			 fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
-/* Fold function call to builtin memchr.  ARG1, ARG2 and LEN are the
-   arguments to the call, and TYPE is its return type.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, INTEGER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      const char *p1;
-
-      if (TREE_CODE (arg2) != INTEGER_CST
-	  || !tree_fits_uhwi_p (len))
-	return NULL_TREE;
-
-      p1 = c_getstr (arg1);
-      if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
-	{
-	  char c;
-	  const char *r;
-	  tree tem;
-
-	  if (target_char_cast (arg2, &c))
-	    return NULL_TREE;
-
-	  r = (const char *) memchr (p1, c, tree_to_uhwi (len));
-
-	  if (r == NULL)
-	    return build_int_cst (TREE_TYPE (arg1), 0);
-
-	  tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
-	  return fold_convert_loc (loc, type, tem);
-	}
-      return NULL_TREE;
-    }
-}
-
 /* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
    Return NULL_TREE if no simplification can be made.  */
 
@@ -8342,9 +8299,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_MEMCHR:
-      return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
-
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8d0acd0..6fd207a 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -100,5 +100,6 @@ extern char target_percent_s_newline[4];
 
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
+extern int target_char_cast (tree, char *);
 
 #endif
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 67a598c..ad20593 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1920,6 +1920,65 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
   return false;
 }
 
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */
+
+static bool
+gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree type = TREE_TYPE (arg1);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  location_t loc = gimple_location (stmt);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    {
+      replace_call_with_value (gsi, build_int_cst (type, 0));
+      return true;
+    }
+
+  if (TREE_CODE (arg2) != INTEGER_CST
+      || !tree_fits_uhwi_p (len))
+    return false;
+
+  const char *p1 = c_getstr (arg1);
+  if (p1)
+    {
+      char c;
+      const char *r;
+
+      if (target_char_cast (arg2, &c))
+	return false;
+
+      r = (const char *) memchr (p1, c, tree_to_uhwi (len));
+
+      if (r == NULL)
+	{
+	  replace_call_with_value (gsi, build_int_cst (type, 0));
+	  return true;
+	}
+      else
+	{
+	  HOST_WIDE_INT offset = r - p1;
+	  if (compare_tree_int (len, offset) <= 0)
+	    {
+	      replace_call_with_value (gsi, build_int_cst (type, 0));
+	      return true;
+	    }
+	  else
+	    {
+	      tree temp = fold_build_pointer_plus_hwi_loc (loc, arg1, offset);
+	      replace_call_with_value (gsi, temp);
+	      return true;
+	    }
+	}
+    }
+
+  return false;
+}
 
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
@@ -3150,6 +3209,8 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
       return gimple_fold_builtin_string_compare (gsi);
     case BUILT_IN_STRNCASECMP:
       return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_MEMCHR:
+      return gimple_fold_builtin_memchr (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 3/3] Test folding of str{n}{case}cmp and memchr (version 2)
  2016-08-17  7:52   ` Martin Liška
@ 2016-10-07  8:42     ` Martin Liška
  2016-10-11  9:39       ` [PATCH] Test folding of str{n}{case}cmp and memchr (version 3) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-07  8:42 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 148 bytes --]

Resending second iteration of the patch.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0003-Test-folding-of-str-n-case-cmp-and-memchr-v3.patch --]
[-- Type: text/x-patch, Size: 4188 bytes --]

From c3df272d22c3a08e19d82c95a95b2fd3e232657c Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 16 Aug 2016 15:56:01 +0200
Subject: [PATCH 3/3] Test folding of str{n}{case}cmp and memchr

gcc/testsuite/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/tree-ssa/builtins-folding.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c | 100 +++++++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c
new file mode 100644
index 0000000..8f7c025
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding.c
@@ -0,0 +1,100 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer2, foo1);
+
+  /* MEMCHR.  */
+  if (__builtin_memchr (foo1, 'x', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (buffer1, 'x', 0) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'o', 11) != foo1 + 4)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'w', 2))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1 + 5, 'o', 6) != foo1 + 7)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 11) != foo1 + 10)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 10))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 12) != foo1 + 11)
+    __builtin_abort ();
+
+  /* STRCMP.  */
+  if (__builtin_strcmp ("hello", "aaaaa") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("", "aaaaa") >= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("ab", "ba") >= 0)
+    __builtin_abort ();
+
+  /* STRCASECMP.  */
+  if (__builtin_strcasecmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp ("aaaaa", "") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp ("", "aaaaa") >= 0)
+    __builtin_abort ();
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("ab", "ba", 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (buffer1, buffer2, 1) != 0)
+    __builtin_abort (); /* not folded away */
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("ab", "ba", 1) >= 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 1) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 100) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strcmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strcasecmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncasecmp" 3 "optimized" } } */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2)
  2016-10-07  8:39   ` [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2) Martin Liška
@ 2016-10-07 10:50     ` Richard Biener
  2016-10-11  9:26       ` Martin Liška
                         ` (3 more replies)
  0 siblings, 4 replies; 45+ messages in thread
From: Richard Biener @ 2016-10-07 10:50 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Fri, Oct 7, 2016 at 10:39 AM, Martin Liška <mliska@suse.cz> wrote:
> I'm resending the patch, where I implemented all builtins mentions in subject
> in gimp-fold.c.
>
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>
> Ready to be installed?

+       case BUILT_IN_STRNCASECMP:
+         {
+           r = strncmp (p1, p2, length);
+           if (r == 0)
+             known_result = true;

length might be -1 here -- I think you need to guard against that (but you can
handle BUILT_IN_STRCASECMP which you miss?).  Likewise for the strncmp case.

Also do we know whether the c_getstr () result is '\0'-terminated?  AFAICS these
constant foldings were not implemented in builtins.c, I see a STRCMP one in
fold-const-call.c though.  I believe the STRING_CST string is not guaranteed to
be '\0' terminated (STRING_CST comes with explicit length).

+      tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str1,
+                                  off0);
+      temp = gimple_build (&stmts, loc, NOP_EXPR, cst_uchar_node, temp);

please don't use gimple_build here, there is nothing to simplify for it.  Using
a NOP_EXPR is also confusing (to match the API...).  Just do
gimple_build_assign (make_ssa_name (...), ..) like other folders do.

+      replace_call_with_value (gsi, fold_convert_loc (loc, type, temp));

and you'd want to replace the call with the MEM_REF stmt using
gsi_replace_with_seq_vops as you fail to set virtual operands properly
above (thus you'll get ICEs when this only matches during GIMPLE opts).

+  location_t loc = gimple_location (stmt);
+  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+  tree cst_uchar_ptr_node
+    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);

it would be nice to not do this tree building if nothign is folded.

+    case BUILT_IN_STRCMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_STRCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_STRNCMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_STRNCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);

please do

+    case BUILT_IN_STRCMP:
+    case BUILT_IN_STRCASECMP:
...
+      return gimple_fold_builtin_string_compare (gsi);

Thanks,
Richard.

> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 2/3] Fold __builtin_memchr (version 2)
  2016-10-07  8:41     ` [PATCH 2/3] Fold __builtin_memchr (version 2) Martin Liška
@ 2016-10-07 11:01       ` Richard Biener
  2016-10-11  9:38         ` [PATCH] Fold __builtin_memchr (version 3) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-07 11:01 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Fri, Oct 7, 2016 at 10:41 AM, Martin Liška <mliska@suse.cz> wrote:
> Resending the patch, where I implemented folding in gimple-fold.c
>
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */

wrong comment

+static bool
+gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)

+       {
+         replace_call_with_value (gsi, build_int_cst (type, 0));

use ptr_type_node (void *) instead of type here and below.

+         HOST_WIDE_INT offset = r - p1;
+         if (compare_tree_int (len, offset) <= 0)
+           {

== 0 can occur in which case we have to return a pointer to the
first char.  I think len < offset can't happen with memchr?

+             replace_call_with_value (gsi, build_int_cst (type, 0));
+             return true;
+           }
+         else
+           {
+             tree temp = fold_build_pointer_plus_hwi_loc (loc, arg1, offset);
+             replace_call_with_value (gsi, temp);

That yields valid GIMPLE by chance, I'd prefer if you'd built that to a
stmt and use the replace-with-vops.

+             return true;
+           }


> Ready to be installed?
> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2)
  2016-10-07 10:50     ` Richard Biener
@ 2016-10-11  9:26       ` Martin Liška
  2016-10-11 10:27         ` Richard Biener
  2016-10-11  9:28       ` [PATCH] Add a helper function: create_tmp Martin Liška
                         ` (2 subsequent siblings)
  3 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-11  9:26 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On 10/07/2016 12:50 PM, Richard Biener wrote:
> On Fri, Oct 7, 2016 at 10:39 AM, Martin Liška <mliska@suse.cz> wrote:
>> I'm resending the patch, where I implemented all builtins mentions in subject
>> in gimp-fold.c.
>>
>> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>>
>> Ready to be installed?
> 
> +       case BUILT_IN_STRNCASECMP:
> +         {
> +           r = strncmp (p1, p2, length);
> +           if (r == 0)
> +             known_result = true;
> 
> length might be -1 here -- I think you need to guard against that (but you can
> handle BUILT_IN_STRCASECMP which you miss?).  Likewise for the strncmp case.

Fixed, I've added comment to STRCASECMP case.

> 
> Also do we know whether the c_getstr () result is '\0'-terminated?  AFAICS these
> constant foldings were not implemented in builtins.c, I see a STRCMP one in
> fold-const-call.c though.  I believe the STRING_CST string is not guaranteed to
> be '\0' terminated (STRING_CST comes with explicit length).

You are absolutely right that we do not have always '\0'-terminated string constants.
Thus I'll send a patch that would return a string from c_getstr just in case
string[string_length] == 0 (separate email with patch will be sent).

> 
> +      tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str1,
> +                                  off0);
> +      temp = gimple_build (&stmts, loc, NOP_EXPR, cst_uchar_node, temp);
> 
> please don't use gimple_build here, there is nothing to simplify for it.  Using
> a NOP_EXPR is also confusing (to match the API...).  Just do
> gimple_build_assign (make_ssa_name (...), ..) like other folders do.
> 
> +      replace_call_with_value (gsi, fold_convert_loc (loc, type, temp));
> 
> and you'd want to replace the call with the MEM_REF stmt using
> gsi_replace_with_seq_vops as you fail to set virtual operands properly
> above (thus you'll get ICEs when this only matches during GIMPLE opts).
> 
> +  location_t loc = gimple_location (stmt);
> +  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
> +  tree cst_uchar_ptr_node
> +    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
> +  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
> 
> it would be nice to not do this tree building if nothign is folded.
> 
> +    case BUILT_IN_STRCMP:
> +      return gimple_fold_builtin_string_compare (gsi);
> +    case BUILT_IN_STRCASECMP:
> +      return gimple_fold_builtin_string_compare (gsi);
> +    case BUILT_IN_STRNCMP:
> +      return gimple_fold_builtin_string_compare (gsi);
> +    case BUILT_IN_STRNCASECMP:
> +      return gimple_fold_builtin_string_compare (gsi);
> 
> please do
> 
> +    case BUILT_IN_STRCMP:
> +    case BUILT_IN_STRCASECMP:
> ...
> +      return gimple_fold_builtin_string_compare (gsi);
> 
> Thanks,
> Richard.

Sure, all notes will be fixed in an email which reply to this one.

Martin

> 
>> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Check \0-termination of string in c_getstr
  2016-10-07 10:50     ` Richard Biener
  2016-10-11  9:26       ` Martin Liška
  2016-10-11  9:28       ` [PATCH] Add a helper function: create_tmp Martin Liška
@ 2016-10-11  9:28       ` Martin Liška
  2016-10-11 10:28         ` Richard Biener
  2016-10-11  9:34       ` [PATCH] Fold __builtin_str{n}{case}cmp functions (version 3) Martin Liška
  3 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-11  9:28 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 165 bytes --]

As mentioned in the email that I reply to, c_getstr should check
null termination of string constants.

Tests of the whole series have been running.

Thanks,
Martin

[-- Attachment #2: 0001-Check-0-termination-of-string-in-c_getstr.patch --]
[-- Type: text/x-patch, Size: 1277 bytes --]

From b446c659e839caa5ea5f36b06ec9110fe69f6e38 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 10 Oct 2016 12:13:12 +0200
Subject: [PATCH 1/5] Check \0-termination of string in c_getstr

gcc/ChangeLog:

2016-10-10  Martin Liska  <mliska@suse.cz>

	* fold-const.c (c_getstr): Guard string termination.
---
 gcc/fold-const.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 02aa484..a9e8650 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -14451,13 +14451,20 @@ c_getstr (tree src)
   if (src == 0)
     return 0;
 
+  unsigned HOST_WIDE_INT string_length = TREE_STRING_LENGTH (src) - 1;
+  const char *string = TREE_STRING_POINTER (src);
+
+  /* If the string is not properly terminated, return 0.  */
+  if (string[string_length] != 0)
+    return 0;
+
   if (offset_node == 0)
-    return TREE_STRING_POINTER (src);
+    return string;
   else if (!tree_fits_uhwi_p (offset_node)
-	   || compare_tree_int (offset_node, TREE_STRING_LENGTH (src) - 1) > 0)
+	   || compare_tree_int (offset_node, string_length) > 0)
     return 0;
 
-  return TREE_STRING_POINTER (src) + tree_to_uhwi (offset_node);
+  return string + tree_to_uhwi (offset_node);
 }
 
 #if CHECKING_P
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Add a helper function: create_tmp
  2016-10-07 10:50     ` Richard Biener
  2016-10-11  9:26       ` Martin Liška
@ 2016-10-11  9:28       ` Martin Liška
  2016-10-11 10:30         ` Richard Biener
  2016-10-11  9:28       ` [PATCH] Check \0-termination of string in c_getstr Martin Liška
  2016-10-11  9:34       ` [PATCH] Fold __builtin_str{n}{case}cmp functions (version 3) Martin Liška
  3 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-11  9:28 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 134 bytes --]

Following patch is a small infrastructure enhancement
in gimple-fold.c.

Tests of the whole series have been running.

Thanks,
Martin

[-- Attachment #2: 0002-Add-a-helper-function-create_tmp.patch --]
[-- Type: text/x-patch, Size: 5970 bytes --]

From cf5983472b8482734393680493293811e5400d6e Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 11 Oct 2016 11:20:33 +0200
Subject: [PATCH 2/5] Add a helper function: create_tmp

gcc/ChangeLog:

2016-10-11  Martin Liska  <mliska@suse.cz>

	* gimple-fold.c (create_tmp): New function.
	(gimple_fold_builtin_memory_op): Use the function.
	(gimple_fold_builtin_strchr): Likewise.
	(gimple_fold_builtin_strcat): Likewise.
	(gimple_build): Likewise.
---
 gcc/gimple-fold.c | 64 ++++++++++++++++++++-----------------------------------
 1 file changed, 23 insertions(+), 41 deletions(-)

diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 3aaa09c..348f331 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -171,6 +171,19 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
   return !node || !node->global.inlined_to;
 }
 
+/* Create a temporary for TYPE for a statement STMT.  If the current function
+   is in SSA form, a SSA name is created.  Otherwise a temporary register
+   is made.  */
+
+static tree
+create_tmp (tree type, gimple *stmt = NULL)
+{
+  if (gimple_in_ssa_p (cfun))
+    return make_ssa_name (type, stmt);
+  else
+    return create_tmp_reg (type);
+}
+
 /* CVAL is value taken from DECL_INITIAL of variable.  Try to transform it into
    acceptable form for is_gimple_min_invariant.
    FROM_DECL (if non-NULL) specify variable whose constructor contains CVAL.  */
@@ -747,11 +760,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 		      if (is_gimple_reg_type (TREE_TYPE (srcmem)))
 			{
 			  new_stmt = gimple_build_assign (NULL_TREE, srcmem);
-			  if (gimple_in_ssa_p (cfun))
-			    srcmem = make_ssa_name (TREE_TYPE (srcmem),
-						    new_stmt);
-			  else
-			    srcmem = create_tmp_reg (TREE_TYPE (srcmem));
+			  srcmem = create_tmp (TREE_TYPE (srcmem), new_stmt);
 			  gimple_assign_set_lhs (new_stmt, srcmem);
 			  gimple_set_vuse (new_stmt, gimple_vuse (stmt));
 			  gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
@@ -1038,10 +1047,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  if (! is_gimple_min_invariant (srcvar))
 	    {
 	      new_stmt = gimple_build_assign (NULL_TREE, srcvar);
-	      if (gimple_in_ssa_p (cfun))
-		srcvar = make_ssa_name (TREE_TYPE (srcvar), new_stmt);
-	      else
-		srcvar = create_tmp_reg (TREE_TYPE (srcvar));
+	      srcvar = create_tmp (TREE_TYPE (srcvar), new_stmt);
 	      gimple_assign_set_lhs (new_stmt, srcvar);
 	      gimple_set_vuse (new_stmt, gimple_vuse (stmt));
 	      gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
@@ -1535,10 +1541,7 @@ gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
   gimple_seq stmts = NULL;
   gimple *new_stmt = gimple_build_call (strlen_fn, 1, str);
   gimple_set_location (new_stmt, loc);
-  if (gimple_in_ssa_p (cfun))
-    len = make_ssa_name (size_type_node);
-  else
-    len = create_tmp_reg (size_type_node);
+  len = create_tmp (size_type_node);
   gimple_call_set_lhs (new_stmt, len);
   gimple_seq_add_stmt_without_update (&stmts, new_stmt);
 
@@ -1611,10 +1614,7 @@ gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
   gimple_seq stmts = NULL, stmts2;
   gimple *repl = gimple_build_call (strlen_fn, 1, dst);
   gimple_set_location (repl, loc);
-  if (gimple_in_ssa_p (cfun))
-    newdst = make_ssa_name (size_type_node);
-  else
-    newdst = create_tmp_reg (size_type_node);
+  newdst = create_tmp (size_type_node);
   gimple_call_set_lhs (repl, newdst);
   gimple_seq_add_stmt_without_update (&stmts, repl);
 
@@ -6376,10 +6376,7 @@ gimple_build (gimple_seq *seq, location_t loc,
   tree res = gimple_simplify (code, type, op0, seq, gimple_build_valueize);
   if (!res)
     {
-      if (gimple_in_ssa_p (cfun))
-	res = make_ssa_name (type);
-      else
-	res = create_tmp_reg (type);
+      res = create_tmp (type);
       gimple *stmt;
       if (code == REALPART_EXPR
 	  || code == IMAGPART_EXPR
@@ -6405,10 +6402,7 @@ gimple_build (gimple_seq *seq, location_t loc,
   tree res = gimple_simplify (code, type, op0, op1, seq, gimple_build_valueize);
   if (!res)
     {
-      if (gimple_in_ssa_p (cfun))
-	res = make_ssa_name (type);
-      else
-	res = create_tmp_reg (type);
+      res = create_tmp (type);
       gimple *stmt = gimple_build_assign (res, code, op0, op1);
       gimple_set_location (stmt, loc);
       gimple_seq_add_stmt_without_update (seq, stmt);
@@ -6429,10 +6423,7 @@ gimple_build (gimple_seq *seq, location_t loc,
 			      seq, gimple_build_valueize);
   if (!res)
     {
-      if (gimple_in_ssa_p (cfun))
-	res = make_ssa_name (type);
-      else
-	res = create_tmp_reg (type);
+      res = create_tmp (type);
       gimple *stmt;
       if (code == BIT_FIELD_REF)
 	stmt = gimple_build_assign (res, code,
@@ -6462,10 +6453,7 @@ gimple_build (gimple_seq *seq, location_t loc,
       gimple *stmt = gimple_build_call (decl, 1, arg0);
       if (!VOID_TYPE_P (type))
 	{
-	  if (gimple_in_ssa_p (cfun))
-	    res = make_ssa_name (type);
-	  else
-	    res = create_tmp_reg (type);
+	  res = create_tmp (type);
 	  gimple_call_set_lhs (stmt, res);
 	}
       gimple_set_location (stmt, loc);
@@ -6491,10 +6479,7 @@ gimple_build (gimple_seq *seq, location_t loc,
       gimple *stmt = gimple_build_call (decl, 2, arg0, arg1);
       if (!VOID_TYPE_P (type))
 	{
-	  if (gimple_in_ssa_p (cfun))
-	    res = make_ssa_name (type);
-	  else
-	    res = create_tmp_reg (type);
+	  res = create_tmp (type);
 	  gimple_call_set_lhs (stmt, res);
 	}
       gimple_set_location (stmt, loc);
@@ -6522,10 +6507,7 @@ gimple_build (gimple_seq *seq, location_t loc,
       gimple *stmt = gimple_build_call (decl, 3, arg0, arg1, arg2);
       if (!VOID_TYPE_P (type))
 	{
-	  if (gimple_in_ssa_p (cfun))
-	    res = make_ssa_name (type);
-	  else
-	    res = create_tmp_reg (type);
+	  res = create_tmp (type);
 	  gimple_call_set_lhs (stmt, res);
 	}
       gimple_set_location (stmt, loc);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Fold __builtin_str{n}{case}cmp functions (version 3)
  2016-10-07 10:50     ` Richard Biener
                         ` (2 preceding siblings ...)
  2016-10-11  9:28       ` [PATCH] Check \0-termination of string in c_getstr Martin Liška
@ 2016-10-11  9:34       ` Martin Liška
  2016-10-12  8:30         ` Richard Biener
  3 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-11  9:34 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 514 bytes --]

Changes from the previous version:

1) Handle BUILT_IN_STRNCMP with length == -1.
2) Direct gimple stmts creation and usage gsi_replace_with_seq_vops.
(hope using of replace_call_with_value is fine if replacing with a cst?)
3) lhs == NULL cases are handled (Or is it fine to replace with a nop in general?
Can change a semantic as it may touch a memory.)
4) CFN_BUILT_IN_STRNCMP can handle strncmp (x, y, 0).
5) Handling of CFN_BUILT_IN_STRNCASECMP is added.

Testing of the whole series has been running.

Martin

[-- Attachment #2: 0003-Fold-__builtin_str-n-case-cmp-functions.patch --]
[-- Type: text/x-patch, Size: 15962 bytes --]

From 925a998bacf2fffb811f1bb655674c869aa45a32 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Wed, 5 Oct 2016 13:18:35 +0200
Subject: [PATCH 3/5] Fold __builtin_str{n}{case}cmp functions

gcc/ChangeLog:

2016-10-06  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_strcmp): Remove function.
	(fold_builtin_strncmp): Likewise.
	(fold_builtin_2): Do not call fold_builtin_strcmp.
	(fold_builtin_3): Do not call fold_builtin_strncmp.
	* fold-const-call.c: Make build_cmp_result global fn.
	* fold-const-call.h: Likewise.
	* gimple-fold.c (gimple_fold_builtin_string_compare): New
	function.
	(gimple_fold_builtin): Call the function.
	* fold-const-call.c (fold_const_call): Handle
	CFN_BUILT_IN_STRCASECMP, CFN_BUILT_IN_STRNCMP and
	CFN_BUILT_IN_STRNCASECMP.
---
 gcc/builtins.c        | 138 -------------------------------------
 gcc/fold-const-call.c |  43 +++++++++---
 gcc/fold-const-call.h |   1 +
 gcc/gimple-fold.c     | 187 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 222 insertions(+), 147 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6c68198..6696f28 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -150,8 +150,6 @@ static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_signbit (tree, rtx);
 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_strncmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
 static tree fold_builtin_isdigit (location_t, tree);
@@ -7333,136 +7331,6 @@ fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
   return NULL_TREE;
 }
 
-/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE))
-    return NULL_TREE;
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return integer_zero_node;
-
-  /* If the second arg is "", return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp
-	= fold_convert_loc (loc, integer_type_node,
-			    build1 (INDIRECT_REF, cst_uchar_node,
-				    fold_convert_loc (loc,
-						      cst_uchar_ptr_node,
-						      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* If the LEN parameter is zero, return zero.  */
-  if (integer_zerop (len))
-    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
-			      arg1, arg2);
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
-
-  /* If the second arg is "", and the length is greater than zero,
-     return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", and the length is greater than zero,
-     return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  /* If len parameter is one, return an expression corresponding to
-     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree ind1 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg1)));
-      tree ind2 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold a call to builtin isascii with argument ARG.  */
 
 static tree
@@ -8391,9 +8259,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     case BUILT_IN_STRCSPN:
       return fold_builtin_strcspn (loc, arg0, arg1);
 
-    case BUILT_IN_STRCMP:
-      return fold_builtin_strcmp (loc, arg0, arg1);
-
     case BUILT_IN_STRPBRK:
       return fold_builtin_strpbrk (loc, arg0, arg1, type);
 
@@ -8475,9 +8340,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_STRNCMP:
-      return fold_builtin_strncmp (loc, arg0, arg1, arg2);
-
     case BUILT_IN_MEMCHR:
       return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
 
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 2bbc887..bbec177 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -69,7 +69,7 @@ host_size_t_cst_p (tree t, size_t *size_out)
    "equal" and > 0 means "more".  Canonicalize it to -1, 0 or 1 and
    return it in type TYPE.  */
 
-static inline tree
+tree
 build_cmp_result (tree type, int res)
 {
   return build_int_cst (type, res < 0 ? -1 : res > 0 ? 1 : 0);
@@ -1397,6 +1397,15 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
 	return build_cmp_result (type, strcmp (p0, p1));
       return NULL_TREE;
 
+    case CFN_BUILT_IN_STRCASECMP:
+      if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
+	{
+	  int r = strcmp (p0, p1);
+	  if (r == 0)
+	    return build_cmp_result (type, r);
+	}
+      return NULL_TREE;
+
     default:
       return fold_const_call_1 (fn, type, arg0, arg1);
     }
@@ -1464,16 +1473,34 @@ tree
 fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
 {
   const char *p0, *p1;
-  size_t s2;
+  size_t s2 = 0;
   switch (fn)
     {
     case CFN_BUILT_IN_STRNCMP:
-      if ((p0 = c_getstr (arg0))
-	  && (p1 = c_getstr (arg1))
-	  && host_size_t_cst_p (arg2, &s2))
-	return build_int_cst (type, strncmp (p0, p1, s2));
-      return NULL_TREE;
-
+      {
+	bool const_size_p = host_size_t_cst_p (arg2, &s2);
+	if (const_size_p && s2 == 0
+	    && !TREE_SIDE_EFFECTS (arg0)
+	    && !TREE_SIDE_EFFECTS (arg1))
+	  return build_int_cst (type, 0);
+	else if ((p0 = c_getstr (arg0))
+	    && (p1 = c_getstr (arg1))
+	    && const_size_p)
+	  return build_int_cst (type, strncmp (p0, p1, s2));
+	return NULL_TREE;
+      }
+    case CFN_BUILT_IN_STRNCASECMP:
+      {
+	bool const_size_p = host_size_t_cst_p (arg2, &s2);
+	if (const_size_p && s2 == 0)
+	  return build_int_cst (type, 0);
+	else if ((p0 = c_getstr (arg0))
+	    && (p1 = c_getstr (arg1))
+	    && const_size_p
+	    && strncmp (p0, p1, s2) == 0)
+	  return build_int_cst (type, 0);
+	return NULL_TREE;
+      }
     case CFN_BUILT_IN_BCMP:
     case CFN_BUILT_IN_MEMCMP:
       if ((p0 = c_getstr (arg0))
diff --git a/gcc/fold-const-call.h b/gcc/fold-const-call.h
index 324ecbf..7f61f2e 100644
--- a/gcc/fold-const-call.h
+++ b/gcc/fold-const-call.h
@@ -24,5 +24,6 @@ tree fold_const_call (combined_fn, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree, tree);
 tree fold_fma (location_t, tree, tree, tree, tree);
+tree build_cmp_result (tree type, int res);
 
 #endif
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 348f331..f892d7e 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -55,7 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-low.h"
 #include "ipa-chkp.h"
 #include "tree-cfg.h"
-
+#include "fold-const-call.h"
 
 /* Return true if T is a constant and the value cast to a target char
    can be represented by a host char.
@@ -1783,6 +1783,186 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
   return true;
 }
 
+static tree
+gimple_load_first_char (location_t loc, tree str, gimple_seq *stmts)
+{
+  tree var;
+
+  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+  tree cst_uchar_ptr_node
+    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
+
+  tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str, off0);
+  gassign *stmt = gimple_build_assign (NULL_TREE, temp);
+  var = create_tmp (cst_uchar_node, stmt);
+
+  gimple_assign_set_lhs (stmt, var);
+  gimple_seq_add_stmt_without_update (stmts, stmt);
+
+  return var;
+}
+
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */
+
+static bool
+gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree callee = gimple_call_fndecl (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+
+  tree type = integer_type_node;
+  tree str1 = gimple_call_arg (stmt, 0);
+  tree str2 = gimple_call_arg (stmt, 1);
+  tree lhs = gimple_call_lhs (stmt);
+  HOST_WIDE_INT length = -1;
+
+  /* Handle strncmp and strncasecmp functions.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_uhwi_p (len))
+	length = tree_to_uhwi (len);
+    }
+
+  const char *p1 = c_getstr (str1);
+  const char *p2 = c_getstr (str2);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (length == 0)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (str1, str2, 0))
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* For known strings, return an immediate value.  */
+  if (p1 && p2)
+    {
+      int r = 0;
+      bool known_result = false;
+
+      switch (fcode)
+	{
+	case BUILT_IN_STRCMP:
+	  {
+	    r = strcmp (p1, p2);
+	    known_result = true;
+	    break;
+	  }
+	case BUILT_IN_STRNCMP:
+	  {
+	    if (length == -1)
+	      break;
+	    r = strncmp (p1, p2, length);
+	    known_result = true;
+	    break;
+	  }
+	/* Only handleable situation is where the string are equal (result 0),
+	   which is already handled by operand_equal_p case.  */
+	case BUILT_IN_STRCASECMP:
+	  break;
+	case BUILT_IN_STRNCASECMP:
+	  {
+	    if (length == -1)
+	      break;
+	    r = strncmp (p1, p2, length);
+	    if (r == 0)
+	      known_result = true;
+	    break;;
+	  }
+	default:
+	  gcc_unreachable ();
+	}
+
+      if (known_result)
+	{
+	  replace_call_with_value (gsi, build_cmp_result (type, r));
+	  return true;
+	}
+    }
+
+  bool nonzero_length = length >= 1
+    || fcode == BUILT_IN_STRCMP
+    || fcode == BUILT_IN_STRCASECMP;
+
+  location_t loc = gimple_location (stmt);
+
+  /* If the second arg is "", return *(const unsigned char*)arg1.  */
+  if (p2 && *p2 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str1, &stmts);
+      if (lhs)
+	stmt = gimple_build_assign (lhs, NOP_EXPR, var);
+      else
+	stmt = gimple_build_nop ();
+
+      gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
+  if (p1 && *p1 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str2, &stmts);
+
+      tree c = create_tmp (integer_type_node);
+      stmt = gimple_build_assign (c, NOP_EXPR, var);
+      gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+      if (lhs)
+	stmt = gimple_build_assign (lhs, NEGATE_EXPR, c);
+      else
+	stmt = gimple_build_nop ();
+      gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg2 - *(const unsigned char*)arg1).  */
+  if (fcode == BUILT_IN_STRNCMP && length == 1)
+    {
+      gimple_seq stmts = NULL;
+      tree temp1 = gimple_load_first_char (loc, str1, &stmts);
+      tree temp2 = gimple_load_first_char (loc, str2, &stmts);
+
+      tree c1 = create_tmp (integer_type_node);
+      gassign *convert1 = gimple_build_assign (c1, NOP_EXPR, temp1);
+      gimple_seq_add_stmt_without_update (&stmts, convert1);
+
+      tree c2 = create_tmp (integer_type_node);
+      gassign *convert2 = gimple_build_assign (c2, NOP_EXPR, temp2);
+      gimple_seq_add_stmt_without_update (&stmts, convert2);
+
+      if (lhs)
+	stmt = gimple_build_assign (lhs, MINUS_EXPR, c1, c2);
+      else
+	stmt = gimple_build_nop ();
+
+      gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  return false;
+}
+
+
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
    by the builtin will be ignored.  UNLOCKED is true is true if this
@@ -3004,6 +3184,11 @@ 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_STRCMP:
+    case BUILT_IN_STRCASECMP:
+    case BUILT_IN_STRNCMP:
+    case BUILT_IN_STRNCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Fold __builtin_memchr (version 3)
  2016-10-07 11:01       ` Richard Biener
@ 2016-10-11  9:38         ` Martin Liška
  2016-10-12  8:34           ` Richard Biener
  2016-10-12  8:36           ` Richard Biener
  0 siblings, 2 replies; 45+ messages in thread
From: Martin Liška @ 2016-10-11  9:38 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1912 bytes --]

On 10/07/2016 01:01 PM, Richard Biener wrote:
> On Fri, Oct 7, 2016 at 10:41 AM, Martin Liška <mliska@suse.cz> wrote:
>> Resending the patch, where I implemented folding in gimple-fold.c
>>
>> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
> 
> +/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
> +   FCODE is the name of the builtin.  */
> 
> wrong comment

Fixed.

> 
> +static bool
> +gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
> 
> +       {
> +         replace_call_with_value (gsi, build_int_cst (type, 0));
> 
> use ptr_type_node (void *) instead of type here and below.

Done.

> 
> +         HOST_WIDE_INT offset = r - p1;
> +         if (compare_tree_int (len, offset) <= 0)
> +           {
> 
> == 0 can occur in which case we have to return a pointer to the
> first char.  I think len < offset can't happen with memchr?

Here I reworked the patch as it's not desired to trigger an undefined behavior
in a host compiler for cases like: memchr ("", 'x', 5). Thus I switched to strchr
and aforementioned hunk would make sense.

> 
> +             replace_call_with_value (gsi, build_int_cst (type, 0));
> +             return true;
> +           }
> +         else
> +           {
> +             tree temp = fold_build_pointer_plus_hwi_loc (loc, arg1, offset);
> +             replace_call_with_value (gsi, temp);
> 
> That yields valid GIMPLE by chance, I'd prefer if you'd built that to a
> stmt and use the replace-with-vops.

Done.

Apart from that I added handling of lhs and the patch supports folding
of CFN_BUILT_IN_MEMCHR.

One question that comes to my mind is whether there's a possibility
to fully test gimple folding of all cases if some of them are already
eaten by generic folding?

Tests of the series have been running.

Martin

> 
> +             return true;
> +           }
> 
> 
>> Ready to be installed?
>> Martin


[-- Attachment #2: 0004-Fold-__builtin_memchr-function.patch --]
[-- Type: text/x-patch, Size: 8404 bytes --]

From 37133bb09ddb23c87bb41ea6fffd5eba5af528fd Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 6 Oct 2016 17:52:45 +0200
Subject: [PATCH 4/5] Fold __builtin_memchr function

gcc/ChangeLog:

2016-10-06  Martin Liska  <mliska@suse.cz>

	* builtins.h(target_char_cst_p): Declare the function.
	* builtins.c (fold_builtin_memchr): Remove.
	(target_char_cst_p): Move the function from gimple-fold.c.
	(fold_builtin_3): Do not call the function.
	* gimple-fold.c (gimple_fold_builtin_memchr): New function.
	(gimple_fold_builtin): Call the function.
	* fold-const-call.c (fold_const_call_1): Handle CFN_BUILT_IN_MEMCHR.
---
 gcc/builtins.c        | 59 +++++++++---------------------------
 gcc/builtins.h        |  1 +
 gcc/fold-const-call.c | 30 ++++++++++++++++++
 gcc/gimple-fold.c     | 84 ++++++++++++++++++++++++++++++++++++++++++---------
 4 files changed, 115 insertions(+), 59 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6696f28..385e78e0 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -148,7 +148,6 @@ static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 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_memchr (location_t, tree, tree, tree, tree);
 static tree fold_builtin_memcmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
@@ -7244,47 +7243,6 @@ fold_builtin_sincos (location_t loc,
 			 fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
-/* Fold function call to builtin memchr.  ARG1, ARG2 and LEN are the
-   arguments to the call, and TYPE is its return type.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, INTEGER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      const char *p1;
-
-      if (TREE_CODE (arg2) != INTEGER_CST
-	  || !tree_fits_uhwi_p (len))
-	return NULL_TREE;
-
-      p1 = c_getstr (arg1);
-      if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
-	{
-	  char c;
-	  const char *r;
-	  tree tem;
-
-	  if (target_char_cast (arg2, &c))
-	    return NULL_TREE;
-
-	  r = (const char *) memchr (p1, c, tree_to_uhwi (len));
-
-	  if (r == NULL)
-	    return build_int_cst (TREE_TYPE (arg1), 0);
-
-	  tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
-	  return fold_convert_loc (loc, type, tem);
-	}
-      return NULL_TREE;
-    }
-}
-
 /* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
    Return NULL_TREE if no simplification can be made.  */
 
@@ -8340,9 +8298,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_MEMCHR:
-      return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
-
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
@@ -9908,3 +9863,17 @@ is_inexpensive_builtin (tree decl)
 
   return false;
 }
+
+/* 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.  */
+
+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;
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8d0acd0..5e83646 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -97,6 +97,7 @@ extern unsigned HOST_WIDE_INT target_percent;
 extern char target_percent_s[3];
 extern char target_percent_c[3];
 extern char target_percent_s_newline[4];
+extern bool target_char_cst_p (tree t, char *p);
 
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index bbec177..3cfab5b 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "fold-const-call.h"
 #include "case-cfn-macros.h"
 #include "tm.h" /* For C[LT]Z_DEFINED_AT_ZERO.  */
+#include "builtins.h"
 
 /* Functions that test for certain constant types, abstracting away the
    decision about whether to check for overflow.  */
@@ -1463,6 +1464,35 @@ fold_const_call_1 (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
       return NULL_TREE;
     }
 
+  switch (fn)
+    {
+    case CFN_BUILT_IN_MEMCHR:
+      {
+	char c;
+	const char *r;
+	if (integer_zerop (arg2))
+	  return build_int_cst (type, 0);
+
+	const char *p1 = c_getstr (arg0);
+	if (!tree_fits_uhwi_p (arg2) || !p1 || !target_char_cst_p (arg1, &c))
+	  return NULL_TREE;
+
+	r = (const char *)strchr (p1, c);
+
+	if (r == NULL)
+	  return build_int_cst (type, 0);
+	else
+	  {
+	    HOST_WIDE_INT offset = r - p1;
+	    if (compare_tree_int (arg2, offset) <= 0)
+	      return build_int_cst (type, 0);
+	  }
+	break;
+      }
+    default:
+      break;
+    }
+
   return NULL_TREE;
 }
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index f892d7e..87464af 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,20 +57,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "fold-const-call.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
@@ -1962,6 +1948,74 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
   return false;
 }
 
+/* Fold a call to the memchr pointed by GSI iterator.  */
+
+static bool
+gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    {
+      replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+      return true;
+    }
+
+  if (TREE_CODE (arg2) != INTEGER_CST
+      || !tree_fits_uhwi_p (len))
+    return false;
+
+  const char *p1 = c_getstr (arg1);
+  if (p1)
+    {
+      char c;
+      const char *r;
+
+      if (!target_char_cst_p (arg2, &c))
+	return false;
+
+      r = (const char *)strchr (p1, c);
+
+      if (r == NULL)
+	{
+	  replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+	  return true;
+	}
+      else
+	{
+	  HOST_WIDE_INT offset = r - p1;
+	  if (compare_tree_int (len, offset) <= 0)
+	    {
+	      replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+	      return true;
+	    }
+	  else
+	    {
+	      gimple_seq stmts = NULL;
+	      if (lhs != NULL_TREE)
+		{
+		  tree offset_cst = build_int_cst (TREE_TYPE (len), offset);
+		  gassign *stmt = gimple_build_assign (lhs, POINTER_PLUS_EXPR,
+						       arg1, offset_cst);
+		  gimple_seq_add_stmt_without_update (&stmts, stmt);
+		}
+	      else
+		gimple_seq_add_stmt_without_update (&stmts,
+						    gimple_build_nop ());
+
+	      gsi_replace_with_seq_vops (gsi, stmts);
+	      return true;
+	    }
+	}
+    }
+
+  return false;
+}
 
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
@@ -3189,6 +3243,8 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_STRNCMP:
     case BUILT_IN_STRNCASECMP:
       return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_MEMCHR:
+      return gimple_fold_builtin_memchr (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Test folding of str{n}{case}cmp and memchr (version 3)
  2016-10-07  8:42     ` [PATCH 3/3] Test folding of str{n}{case}cmp and memchr (version 2) Martin Liška
@ 2016-10-11  9:39       ` Martin Liška
  2016-10-12  8:35         ` Richard Biener
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-11  9:39 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 87 bytes --]

Third iteration of tests, where I added both GENERIC and GIMPLE folding
tests.

Martin

[-- Attachment #2: 0005-Test-folding-of-str-n-case-cmp-and-memchr.patch --]
[-- Type: text/x-patch, Size: 7942 bytes --]

From ac9020c31a6f5291c896a90aae594dd564420d95 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 16 Aug 2016 15:56:01 +0200
Subject: [PATCH 5/5] Test folding of str{n}{case}cmp and memchr

gcc/testsuite/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/tree-ssa/builtins-folding-generic.c: New test.
	* gcc.dg/tree-ssa/builtins-folding-gimple.c: New test.
---
 .../gcc.dg/tree-ssa/builtins-folding-generic.c     |  76 +++++++++++++
 .../gcc.dg/tree-ssa/builtins-folding-gimple.c      | 126 +++++++++++++++++++++
 2 files changed, 202 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
new file mode 100644
index 0000000..387be83
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
@@ -0,0 +1,76 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-original" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer2, foo1);
+
+  /* MEMCHR.  */
+  if (__builtin_memchr ("hello world", 'x', 11))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'x', 0) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'w', 2))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'd', 10))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", '\0', 11))
+    __builtin_abort ();
+
+  /* STRCMP.  */
+  if (__builtin_strcmp ("hello", "aaaaa") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("", "aaaaa") >= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("ab", "ba") >= 0)
+    __builtin_abort ();
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("ab", "ba", 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+
+  /* STRCASECMP.  */
+  if (__builtin_strcasecmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strcmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strcasecmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncasecmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "original" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c
new file mode 100644
index 0000000..8a917bf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c
@@ -0,0 +1,126 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer2, foo1);
+
+  /* MEMCHR.  */
+  if (__builtin_memchr ("", 'x', 1000))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'x', 1000))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'x', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (buffer1, 'x', 0) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'o', 11) != foo1 + 4)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'w', 2))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1 + 5, 'o', 6) != foo1 + 7)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 11) != foo1 + 10)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'd', 10))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, '\0', 12) != foo1 + 11)
+    __builtin_abort ();
+
+  __builtin_memchr (foo1, 'x', 11);
+  __builtin_memchr (buffer1, 'x', 0);
+  __builtin_memchr (foo1, 'w', 2);
+  __builtin_memchr (foo1, 'e', 5);
+
+  /* STRCMP.  */
+  if (__builtin_strcmp ("hello", "aaaaa") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("", "aaaaa") >= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("ab", "ba") >= 0)
+    __builtin_abort ();
+
+  __builtin_strcmp ("hello", "aaaaa");
+  __builtin_strcmp ("aaaaa", "aaaaa");
+  __builtin_strcmp ("aaaaa", "");
+  __builtin_strcmp ("", "aaaaa");
+  __builtin_strcmp ("ab", "ba");
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("ab", "ba", 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (buffer1, buffer2, 1) != 0)
+    __builtin_abort (); /* not folded away */
+
+  __builtin_strncmp ("hello", "aaaaa", 0);
+  __builtin_strncmp ("aaaaa", "aaaaa", 100);
+  __builtin_strncmp ("aaaaa", "", 100);
+  __builtin_strncmp ("", "aaaaa", 100);
+  __builtin_strncmp ("ab", "ba", 1);
+  __builtin_strncmp ("aab", "aac", 2);
+  __builtin_strncmp (buffer1, buffer2, 0);
+  __builtin_strncmp (buffer1, buffer2, 1);
+  __builtin_strncmp ("", buffer2, 1);
+  __builtin_strncmp (buffer1, "", 1);
+
+  /* STRCASECMP.  */
+  if (__builtin_strcasecmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp ("aaaaa", "") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp ("", "aaaaa") >= 0)
+    __builtin_abort ();
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("ab", "ba", 1) >= 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 1) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 100) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strcmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strcasecmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncasecmp" 3 "optimized" } } */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2)
  2016-10-11  9:26       ` Martin Liška
@ 2016-10-11 10:27         ` Richard Biener
  0 siblings, 0 replies; 45+ messages in thread
From: Richard Biener @ 2016-10-11 10:27 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 11:26 AM, Martin Liška <mliska@suse.cz> wrote:
> On 10/07/2016 12:50 PM, Richard Biener wrote:
>> On Fri, Oct 7, 2016 at 10:39 AM, Martin Liška <mliska@suse.cz> wrote:
>>> I'm resending the patch, where I implemented all builtins mentions in subject
>>> in gimp-fold.c.
>>>
>>> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>>>
>>> Ready to be installed?
>>
>> +       case BUILT_IN_STRNCASECMP:
>> +         {
>> +           r = strncmp (p1, p2, length);
>> +           if (r == 0)
>> +             known_result = true;
>>
>> length might be -1 here -- I think you need to guard against that (but you can
>> handle BUILT_IN_STRCASECMP which you miss?).  Likewise for the strncmp case.
>
> Fixed, I've added comment to STRCASECMP case.
>
>>
>> Also do we know whether the c_getstr () result is '\0'-terminated?  AFAICS these
>> constant foldings were not implemented in builtins.c, I see a STRCMP one in
>> fold-const-call.c though.  I believe the STRING_CST string is not guaranteed to
>> be '\0' terminated (STRING_CST comes with explicit length).
>
> You are absolutely right that we do not have always '\0'-terminated string constants.
> Thus I'll send a patch that would return a string from c_getstr just in case
> string[string_length] == 0 (separate email with patch will be sent).

Maybe add a second output to c_getstr to pass down the string length
in case it is known?

In this case you could use strN* () variants for constant folding.
"not found" would need
to be folded conditional on null termination to avoid folding
undefined behavior.

Richard.

>>
>> +      tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str1,
>> +                                  off0);
>> +      temp = gimple_build (&stmts, loc, NOP_EXPR, cst_uchar_node, temp);
>>
>> please don't use gimple_build here, there is nothing to simplify for it.  Using
>> a NOP_EXPR is also confusing (to match the API...).  Just do
>> gimple_build_assign (make_ssa_name (...), ..) like other folders do.
>>
>> +      replace_call_with_value (gsi, fold_convert_loc (loc, type, temp));
>>
>> and you'd want to replace the call with the MEM_REF stmt using
>> gsi_replace_with_seq_vops as you fail to set virtual operands properly
>> above (thus you'll get ICEs when this only matches during GIMPLE opts).
>>
>> +  location_t loc = gimple_location (stmt);
>> +  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
>> +  tree cst_uchar_ptr_node
>> +    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
>> +  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
>>
>> it would be nice to not do this tree building if nothign is folded.
>>
>> +    case BUILT_IN_STRCMP:
>> +      return gimple_fold_builtin_string_compare (gsi);
>> +    case BUILT_IN_STRCASECMP:
>> +      return gimple_fold_builtin_string_compare (gsi);
>> +    case BUILT_IN_STRNCMP:
>> +      return gimple_fold_builtin_string_compare (gsi);
>> +    case BUILT_IN_STRNCASECMP:
>> +      return gimple_fold_builtin_string_compare (gsi);
>>
>> please do
>>
>> +    case BUILT_IN_STRCMP:
>> +    case BUILT_IN_STRCASECMP:
>> ...
>> +      return gimple_fold_builtin_string_compare (gsi);
>>
>> Thanks,
>> Richard.
>
> Sure, all notes will be fixed in an email which reply to this one.
>
> Martin
>
>>
>>> Martin
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Check \0-termination of string in c_getstr
  2016-10-11  9:28       ` [PATCH] Check \0-termination of string in c_getstr Martin Liška
@ 2016-10-11 10:28         ` Richard Biener
  2016-10-12 13:14           ` Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-11 10:28 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 11:27 AM, Martin Liška <mliska@suse.cz> wrote:
> As mentioned in the email that I reply to, c_getstr should check
> null termination of string constants.
>
> Tests of the whole series have been running.

Looks ok to me (if testing passes).

Thanks,
Richard.

> Thanks,
> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Add a helper function: create_tmp
  2016-10-11  9:28       ` [PATCH] Add a helper function: create_tmp Martin Liška
@ 2016-10-11 10:30         ` Richard Biener
  2016-10-11 10:31           ` Richard Biener
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-11 10:30 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 11:28 AM, Martin Liška <mliska@suse.cz> wrote:
> Following patch is a small infrastructure enhancement
> in gimple-fold.c.
>
> Tests of the whole series have been running.

Sorry to be picky, but can you rename it to create_reg_tmp?

Ok with that change.

RIchard.

> Thanks,
> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Add a helper function: create_tmp
  2016-10-11 10:30         ` Richard Biener
@ 2016-10-11 10:31           ` Richard Biener
  2016-10-12 10:50             ` Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-11 10:31 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 12:29 PM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Tue, Oct 11, 2016 at 11:28 AM, Martin Liška <mliska@suse.cz> wrote:
>> Following patch is a small infrastructure enhancement
>> in gimple-fold.c.
>>
>> Tests of the whole series have been running.
>
> Sorry to be picky, but can you rename it to create_reg_tmp?

Hrm.  Too easy to confuse with create_tmp_reg ... so maybe
create_tmp_reg_or_ssa_name?

> Ok with that change.
>
> RIchard.
>
>> Thanks,
>> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Fold __builtin_str{n}{case}cmp functions (version 3)
  2016-10-11  9:34       ` [PATCH] Fold __builtin_str{n}{case}cmp functions (version 3) Martin Liška
@ 2016-10-12  8:30         ` Richard Biener
  2016-10-12 13:17           ` Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-12  8:30 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 11:33 AM, Martin Liška <mliska@suse.cz> wrote:
> Changes from the previous version:
>
> 1) Handle BUILT_IN_STRNCMP with length == -1.
> 2) Direct gimple stmts creation and usage gsi_replace_with_seq_vops.
> (hope using of replace_call_with_value is fine if replacing with a cst?)

yes

> 3) lhs == NULL cases are handled (Or is it fine to replace with a nop in general?
> Can change a semantic as it may touch a memory.)
> 4) CFN_BUILT_IN_STRNCMP can handle strncmp (x, y, 0).
> 5) Handling of CFN_BUILT_IN_STRNCASECMP is added.
>
> Testing of the whole series has been running.

gimple_load_first_char needs a comment.

+      tree var = gimple_load_first_char (loc, str1, &stmts);
+      if (lhs)
+       stmt = gimple_build_assign (lhs, NOP_EXPR, var);
+      else
+       stmt = gimple_build_nop ();

I think you don't need the nop() as you have at least one stmt
from the load.  Likewise for the other cases below.

Otherwise this patch looks ok now.

Richard.



> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Fold __builtin_memchr (version 3)
  2016-10-11  9:38         ` [PATCH] Fold __builtin_memchr (version 3) Martin Liška
@ 2016-10-12  8:34           ` Richard Biener
  2016-10-12  8:36           ` Richard Biener
  1 sibling, 0 replies; 45+ messages in thread
From: Richard Biener @ 2016-10-12  8:34 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 11:38 AM, Martin Liška <mliska@suse.cz> wrote:
> On 10/07/2016 01:01 PM, Richard Biener wrote:
>> On Fri, Oct 7, 2016 at 10:41 AM, Martin Liška <mliska@suse.cz> wrote:
>>> Resending the patch, where I implemented folding in gimple-fold.c
>>>
>>> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>>
>> +/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
>> +   FCODE is the name of the builtin.  */
>>
>> wrong comment
>
> Fixed.
>
>>
>> +static bool
>> +gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
>>
>> +       {
>> +         replace_call_with_value (gsi, build_int_cst (type, 0));
>>
>> use ptr_type_node (void *) instead of type here and below.
>
> Done.
>
>>
>> +         HOST_WIDE_INT offset = r - p1;
>> +         if (compare_tree_int (len, offset) <= 0)
>> +           {
>>
>> == 0 can occur in which case we have to return a pointer to the
>> first char.  I think len < offset can't happen with memchr?
>
> Here I reworked the patch as it's not desired to trigger an undefined behavior
> in a host compiler for cases like: memchr ("", 'x', 5). Thus I switched to strchr
> and aforementioned hunk would make sense.
>
>>
>> +             replace_call_with_value (gsi, build_int_cst (type, 0));
>> +             return true;
>> +           }
>> +         else
>> +           {
>> +             tree temp = fold_build_pointer_plus_hwi_loc (loc, arg1, offset);
>> +             replace_call_with_value (gsi, temp);
>>
>> That yields valid GIMPLE by chance, I'd prefer if you'd built that to a
>> stmt and use the replace-with-vops.
>
> Done.
>
> Apart from that I added handling of lhs and the patch supports folding
> of CFN_BUILT_IN_MEMCHR.
>
> One question that comes to my mind is whether there's a possibility
> to fully test gimple folding of all cases if some of them are already
> eaten by generic folding?
>
> Tests of the series have been running.

Ok.

Thanks,
Richard.

> Martin
>
>>
>> +             return true;
>> +           }
>>
>>
>>> Ready to be installed?
>>> Martin
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Test folding of str{n}{case}cmp and memchr (version 3)
  2016-10-11  9:39       ` [PATCH] Test folding of str{n}{case}cmp and memchr (version 3) Martin Liška
@ 2016-10-12  8:35         ` Richard Biener
  2016-10-12 13:20           ` Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-12  8:35 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 11:38 AM, Martin Liška <mliska@suse.cz> wrote:
> Third iteration of tests, where I added both GENERIC and GIMPLE folding
> tests.

They should work already with -O1?

Otherwise ok.

Richard.

> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Fold __builtin_memchr (version 3)
  2016-10-11  9:38         ` [PATCH] Fold __builtin_memchr (version 3) Martin Liška
  2016-10-12  8:34           ` Richard Biener
@ 2016-10-12  8:36           ` Richard Biener
  2016-10-12 13:19             ` Martin Liška
  1 sibling, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-12  8:36 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Oct 11, 2016 at 11:38 AM, Martin Liška <mliska@suse.cz> wrote:
> One question that comes to my mind is whether there's a possibility
> to fully test gimple folding of all cases if some of them are already
> eaten by generic folding?

The only way is to make GENERIC folding not trigger by pushing
constants to temporaries.

Richard.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Add a helper function: create_tmp
  2016-10-11 10:31           ` Richard Biener
@ 2016-10-12 10:50             ` Martin Liška
  0 siblings, 0 replies; 45+ messages in thread
From: Martin Liška @ 2016-10-12 10:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On 10/11/2016 12:31 PM, Richard Biener wrote:
> Hrm.  Too easy to confuse with create_tmp_reg ... so maybe
> create_tmp_reg_or_ssa_name?

Yep, renamed function patch installed as r241030.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Check \0-termination of string in c_getstr
  2016-10-11 10:28         ` Richard Biener
@ 2016-10-12 13:14           ` Martin Liška
  2016-10-13 15:24             ` [PATCH] Check \0-termination of string in c_getstr (simplified version) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-12 13:14 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 684 bytes --]

On 10/11/2016 12:28 PM, Richard Biener wrote:
> On Tue, Oct 11, 2016 at 11:27 AM, Martin Liška <mliska@suse.cz> wrote:
>> As mentioned in the email that I reply to, c_getstr should check
>> null termination of string constants.
>>
>> Tests of the whole series have been running.
> 
> Looks ok to me (if testing passes).

Thanks for the review, however I decided to enhance the API to support
a requested length argument (if a string is not null terminated)
and to return length of the string(usable for strn* functions folding).

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests as
a whole series.

Martin

> 
> Thanks,
> Richard.
> 
>> Thanks,
>> Martin


[-- Attachment #2: 0002-Enhance-c_getstr-API.patch --]
[-- Type: text/x-patch, Size: 3289 bytes --]

From e6c16ea038104ef15b087ff9fca23d9b2406e65e Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 10 Oct 2016 12:13:12 +0200
Subject: [PATCH] Enhance c_getstr API

gcc/ChangeLog:

2016-10-10  Martin Liska  <mliska@suse.cz>

	* fold-const.c (c_getstr): Guard string termination, or validate
	that requested length is within a string constant.
	* fold-const.h (c_getstr): Set default value for the new arg.
---
 gcc/fold-const.c | 44 +++++++++++++++++++++++++++++++++++---------
 gcc/fold-const.h |  3 ++-
 2 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 02aa484..eb53e84 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -14440,24 +14440,50 @@ fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE_INT off)
 }
 
 /* Return a char pointer for a C string if it is a string constant
-   or sum of string constant and integer constant.  */
+   or sum of string constant and integer constant.
+   If the string constant is properly zero-terminated, the constant is returned.
+   Otherwise, if REQ_LENGTH is a non-negative number, the constant
+   is returned if the string length is greater or equal to REQ_LENGTH.
+   If STRLEN is a valid pointer, length (including terminatinch character)
+   of returned string is stored to the argument.  */
 
 const char *
-c_getstr (tree src)
+c_getstr (tree src, HOST_WIDE_INT req_length, unsigned HOST_WIDE_INT *strlen)
 {
   tree offset_node;
 
+  if (strlen)
+    *strlen = 0;
+
   src = string_constant (src, &offset_node);
   if (src == 0)
-    return 0;
+    return NULL;
 
-  if (offset_node == 0)
-    return TREE_STRING_POINTER (src);
-  else if (!tree_fits_uhwi_p (offset_node)
-	   || compare_tree_int (offset_node, TREE_STRING_LENGTH (src) - 1) > 0)
-    return 0;
+  unsigned HOST_WIDE_INT offset = 0;
+  if (offset_node != NULL_TREE)
+    {
+      if (!tree_fits_uhwi_p (offset_node))
+	return NULL;
+      else
+	offset = tree_to_uhwi (offset_node);
+    }
 
-  return TREE_STRING_POINTER (src) + tree_to_uhwi (offset_node);
+  unsigned HOST_WIDE_INT string_length = TREE_STRING_LENGTH (src);
+  const char *string = TREE_STRING_POINTER (src);
+  if (offset > string_length)
+    return NULL;
+
+  /* If the string is properly '\0' character terminated, return it.  */
+  if ((string_length > 0 && string[string_length - 1] == 0)
+      || (req_length != -1
+	  && (unsigned HOST_WIDE_INT)req_length <= string_length - offset))
+    {
+      if (strlen)
+	*strlen = string_length - offset;
+      return string + offset;
+    }
+  else
+    return NULL;
 }
 
 #if CHECKING_P
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index 637e46b..bbf831a 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -182,7 +182,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);
+extern const char *c_getstr (tree src, HOST_WIDE_INT req_length = -1,
+			     unsigned HOST_WIDE_INT *strlen = NULL);
 
 /* Return OFF converted to a pointer offset type suitable as offset for
    POINTER_PLUS_EXPR.  Use location LOC for this conversion.  */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Fold __builtin_str{n}{case}cmp functions (version 3)
  2016-10-12  8:30         ` Richard Biener
@ 2016-10-12 13:17           ` Martin Liška
  2016-10-13 15:25             ` [PATCH] Fold __builtin_str{n}{case}cmp functions (simplified version 4) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-12 13:17 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1318 bytes --]

On 10/12/2016 10:30 AM, Richard Biener wrote:
> On Tue, Oct 11, 2016 at 11:33 AM, Martin Liška <mliska@suse.cz> wrote:
>> Changes from the previous version:
>>
>> 1) Handle BUILT_IN_STRNCMP with length == -1.
>> 2) Direct gimple stmts creation and usage gsi_replace_with_seq_vops.
>> (hope using of replace_call_with_value is fine if replacing with a cst?)
> 
> yes
> 
>> 3) lhs == NULL cases are handled (Or is it fine to replace with a nop in general?
>> Can change a semantic as it may touch a memory.)
>> 4) CFN_BUILT_IN_STRNCMP can handle strncmp (x, y, 0).
>> 5) Handling of CFN_BUILT_IN_STRNCASECMP is added.
>>
>> Testing of the whole series has been running.
> 
> gimple_load_first_char needs a comment.
> 
> +      tree var = gimple_load_first_char (loc, str1, &stmts);
> +      if (lhs)
> +       stmt = gimple_build_assign (lhs, NOP_EXPR, var);
> +      else
> +       stmt = gimple_build_nop ();
> 
> I think you don't need the nop() as you have at least one stmt
> from the load.  Likewise for the other cases below.

Thanks, both nits are fixed in the new version.
Apart from that, I added usages of enhanced c_getstr API.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests
as a whole series.

Martin

> 
> Otherwise this patch looks ok now.
> 
> Richard.
> 
> 
> 
>> Martin


[-- Attachment #2: 0003-Fold-__builtin_str-n-case-cmp-functions-v4.patch --]
[-- Type: text/x-patch, Size: 16082 bytes --]

From ec78ca3e97984acd8ef6330424798218568bd6c8 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Wed, 5 Oct 2016 13:18:35 +0200
Subject: [PATCH 3/5] Fold __builtin_str{n}{case}cmp functions

gcc/ChangeLog:

2016-10-06  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_strcmp): Remove function.
	(fold_builtin_strncmp): Likewise.
	(fold_builtin_2): Do not call fold_builtin_strcmp.
	(fold_builtin_3): Do not call fold_builtin_strncmp.
	* fold-const-call.c: Make build_cmp_result global fn.
	* fold-const-call.h: Likewise.
	* gimple-fold.c (gimple_fold_builtin_string_compare): New
	function.
	(gimple_fold_builtin): Call the function.
	* fold-const-call.c (fold_const_call): Handle
	CFN_BUILT_IN_STRCASECMP, CFN_BUILT_IN_STRNCMP and
	CFN_BUILT_IN_STRNCASECMP.
---
 gcc/builtins.c        | 138 ------------------------------------
 gcc/fold-const-call.c |  43 +++++++++---
 gcc/fold-const-call.h |   1 +
 gcc/gimple-fold.c     | 189 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 224 insertions(+), 147 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6c68198..6696f28 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -150,8 +150,6 @@ static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_signbit (tree, rtx);
 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_strncmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
 static tree fold_builtin_isdigit (location_t, tree);
@@ -7333,136 +7331,6 @@ fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
   return NULL_TREE;
 }
 
-/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE))
-    return NULL_TREE;
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return integer_zero_node;
-
-  /* If the second arg is "", return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp
-	= fold_convert_loc (loc, integer_type_node,
-			    build1 (INDIRECT_REF, cst_uchar_node,
-				    fold_convert_loc (loc,
-						      cst_uchar_ptr_node,
-						      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* If the LEN parameter is zero, return zero.  */
-  if (integer_zerop (len))
-    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
-			      arg1, arg2);
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
-
-  /* If the second arg is "", and the length is greater than zero,
-     return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", and the length is greater than zero,
-     return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  /* If len parameter is one, return an expression corresponding to
-     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree ind1 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg1)));
-      tree ind2 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold a call to builtin isascii with argument ARG.  */
 
 static tree
@@ -8391,9 +8259,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     case BUILT_IN_STRCSPN:
       return fold_builtin_strcspn (loc, arg0, arg1);
 
-    case BUILT_IN_STRCMP:
-      return fold_builtin_strcmp (loc, arg0, arg1);
-
     case BUILT_IN_STRPBRK:
       return fold_builtin_strpbrk (loc, arg0, arg1, type);
 
@@ -8475,9 +8340,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_STRNCMP:
-      return fold_builtin_strncmp (loc, arg0, arg1, arg2);
-
     case BUILT_IN_MEMCHR:
       return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
 
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 2bbc887..993769c 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -69,7 +69,7 @@ host_size_t_cst_p (tree t, size_t *size_out)
    "equal" and > 0 means "more".  Canonicalize it to -1, 0 or 1 and
    return it in type TYPE.  */
 
-static inline tree
+tree
 build_cmp_result (tree type, int res)
 {
   return build_int_cst (type, res < 0 ? -1 : res > 0 ? 1 : 0);
@@ -1397,6 +1397,15 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
 	return build_cmp_result (type, strcmp (p0, p1));
       return NULL_TREE;
 
+    case CFN_BUILT_IN_STRCASECMP:
+      if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
+	{
+	  int r = strcmp (p0, p1);
+	  if (r == 0)
+	    return build_cmp_result (type, r);
+	}
+      return NULL_TREE;
+
     default:
       return fold_const_call_1 (fn, type, arg0, arg1);
     }
@@ -1464,16 +1473,34 @@ tree
 fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
 {
   const char *p0, *p1;
-  size_t s2;
+  size_t s2 = 0;
   switch (fn)
     {
     case CFN_BUILT_IN_STRNCMP:
-      if ((p0 = c_getstr (arg0))
-	  && (p1 = c_getstr (arg1))
-	  && host_size_t_cst_p (arg2, &s2))
-	return build_int_cst (type, strncmp (p0, p1, s2));
-      return NULL_TREE;
-
+      {
+	bool const_size_p = host_size_t_cst_p (arg2, &s2);
+	if (const_size_p && s2 == 0
+	    && !TREE_SIDE_EFFECTS (arg0)
+	    && !TREE_SIDE_EFFECTS (arg1))
+	  return build_int_cst (type, 0);
+	else if (const_size_p
+		 && (p0 = c_getstr (arg0, s2))
+		 && (p1 = c_getstr (arg1, s2)))
+	  return build_int_cst (type, strncmp (p0, p1, s2));
+	return NULL_TREE;
+      }
+    case CFN_BUILT_IN_STRNCASECMP:
+      {
+	bool const_size_p = host_size_t_cst_p (arg2, &s2);
+	if (const_size_p && s2 == 0)
+	  return build_int_cst (type, 0);
+	else if (const_size_p
+		 && (p0 = c_getstr (arg0, s2))
+		 && (p1 = c_getstr (arg1, s2))
+		 && strncmp (p0, p1, s2) == 0)
+	  return build_int_cst (type, 0);
+	return NULL_TREE;
+      }
     case CFN_BUILT_IN_BCMP:
     case CFN_BUILT_IN_MEMCMP:
       if ((p0 = c_getstr (arg0))
diff --git a/gcc/fold-const-call.h b/gcc/fold-const-call.h
index 324ecbf..7f61f2e 100644
--- a/gcc/fold-const-call.h
+++ b/gcc/fold-const-call.h
@@ -24,5 +24,6 @@ tree fold_const_call (combined_fn, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree, tree);
 tree fold_fma (location_t, tree, tree, tree, tree);
+tree build_cmp_result (tree type, int res);
 
 #endif
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 1836927..62bd593 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -55,7 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-low.h"
 #include "ipa-chkp.h"
 #include "tree-cfg.h"
-
+#include "fold-const-call.h"
 
 /* Return true if T is a constant and the value cast to a target char
    can be represented by a host char.
@@ -1786,6 +1786,188 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Build and append gimple statements to STMTS that would load a first
+   character of a memory location identified by STR.  LOC is location
+   of the statement.  */
+
+static tree
+gimple_load_first_char (location_t loc, tree str, gimple_seq *stmts)
+{
+  tree var;
+
+  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+  tree cst_uchar_ptr_node
+    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
+
+  tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str, off0);
+  gassign *stmt = gimple_build_assign (NULL_TREE, temp);
+  var = create_tmp_reg_or_ssa_name (cst_uchar_node, stmt);
+
+  gimple_assign_set_lhs (stmt, var);
+  gimple_seq_add_stmt_without_update (stmts, stmt);
+
+  return var;
+}
+
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */
+
+static bool
+gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree callee = gimple_call_fndecl (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+
+  tree type = integer_type_node;
+  tree str1 = gimple_call_arg (stmt, 0);
+  tree str2 = gimple_call_arg (stmt, 1);
+  tree lhs = gimple_call_lhs (stmt);
+  HOST_WIDE_INT length = -1;
+
+  /* Handle strncmp and strncasecmp functions.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_uhwi_p (len))
+	length = tree_to_uhwi (len);
+    }
+
+  const char *p1 = c_getstr (str1, length);
+  const char *p2 = c_getstr (str2, length);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (length == 0)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (str1, str2, 0))
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* For known strings, return an immediate value.  */
+  if (p1 && p2)
+    {
+      int r = 0;
+      bool known_result = false;
+
+      switch (fcode)
+	{
+	case BUILT_IN_STRCMP:
+	  {
+	    r = strcmp (p1, p2);
+	    known_result = true;
+	    break;
+	  }
+	case BUILT_IN_STRNCMP:
+	  {
+	    if (length == -1)
+	      break;
+	    r = strncmp (p1, p2, length);
+	    known_result = true;
+	    break;
+	  }
+	/* Only handleable situation is where the string are equal (result 0),
+	   which is already handled by operand_equal_p case.  */
+	case BUILT_IN_STRCASECMP:
+	  break;
+	case BUILT_IN_STRNCASECMP:
+	  {
+	    if (length == -1)
+	      break;
+	    r = strncmp (p1, p2, length);
+	    if (r == 0)
+	      known_result = true;
+	    break;;
+	  }
+	default:
+	  gcc_unreachable ();
+	}
+
+      if (known_result)
+	{
+	  replace_call_with_value (gsi, build_cmp_result (type, r));
+	  return true;
+	}
+    }
+
+  bool nonzero_length = length >= 1
+    || fcode == BUILT_IN_STRCMP
+    || fcode == BUILT_IN_STRCASECMP;
+
+  location_t loc = gimple_location (stmt);
+
+  /* If the second arg is "", return *(const unsigned char*)arg1.  */
+  if (p2 && *p2 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str1, &stmts);
+      if (lhs)
+	{
+	  stmt = gimple_build_assign (lhs, NOP_EXPR, var);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+	}
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
+  if (p1 && *p1 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str2, &stmts);
+
+      if (lhs)
+	{
+	  tree c = create_tmp_reg_or_ssa_name (integer_type_node);
+	  stmt = gimple_build_assign (c, NOP_EXPR, var);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+	  stmt = gimple_build_assign (lhs, NEGATE_EXPR, c);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+	}
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg2 - *(const unsigned char*)arg1).  */
+  if (fcode == BUILT_IN_STRNCMP && length == 1)
+    {
+      gimple_seq stmts = NULL;
+      tree temp1 = gimple_load_first_char (loc, str1, &stmts);
+      tree temp2 = gimple_load_first_char (loc, str2, &stmts);
+
+      if (lhs)
+	{
+	  tree c1 = create_tmp_reg_or_ssa_name (integer_type_node);
+	  gassign *convert1 = gimple_build_assign (c1, NOP_EXPR, temp1);
+	  gimple_seq_add_stmt_without_update (&stmts, convert1);
+
+	  tree c2 = create_tmp_reg_or_ssa_name (integer_type_node);
+	  gassign *convert2 = gimple_build_assign (c2, NOP_EXPR, temp2);
+	  gimple_seq_add_stmt_without_update (&stmts, convert2);
+
+	  stmt = gimple_build_assign (lhs, MINUS_EXPR, c1, c2);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+	}
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  return false;
+}
+
+
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
    by the builtin will be ignored.  UNLOCKED is true is true if this
@@ -3007,6 +3189,11 @@ 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_STRCMP:
+    case BUILT_IN_STRCASECMP:
+    case BUILT_IN_STRNCMP:
+    case BUILT_IN_STRNCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Fold __builtin_memchr (version 3)
  2016-10-12  8:36           ` Richard Biener
@ 2016-10-12 13:19             ` Martin Liška
  2016-10-13 15:26               ` [PATCH] Fold __builtin_memchr (simplified version 4) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-12 13:19 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 670 bytes --]

On 10/12/2016 10:35 AM, Richard Biener wrote:
> On Tue, Oct 11, 2016 at 11:38 AM, Martin Liška <mliska@suse.cz> wrote:
>> One question that comes to my mind is whether there's a possibility
>> to fully test gimple folding of all cases if some of them are already
>> eaten by generic folding?
> 
> The only way is to make GENERIC folding not trigger by pushing
> constants to temporaries.
> 
> Richard.
> 

Good idea, I've done that in the patch with tests. I made a small revision to
patch, where I utilize the new c_getstr function arguments to handle more
cases.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests
as a whole series.

Martin

[-- Attachment #2: 0004-Fold-__builtin_memchr-function-v4.patch --]
[-- Type: text/x-patch, Size: 8883 bytes --]

From bc96a7b30764a098d47fa65bd1682005111febdf Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 6 Oct 2016 17:52:45 +0200
Subject: [PATCH 4/5] Fold __builtin_memchr function

gcc/ChangeLog:

2016-10-06  Martin Liska  <mliska@suse.cz>

	* builtins.h(target_char_cst_p): Declare the function.
	* builtins.c (fold_builtin_memchr): Remove.
	(target_char_cst_p): Move the function from gimple-fold.c.
	(fold_builtin_3): Do not call the function.
	* gimple-fold.c (gimple_fold_builtin_memchr): New function.
	(gimple_fold_builtin): Call the function.
	* fold-const-call.c (fold_const_call_1): Handle CFN_BUILT_IN_MEMCHR.
---
 gcc/builtins.c        | 59 +++++++++---------------------------
 gcc/builtins.h        |  1 +
 gcc/fold-const-call.c | 41 +++++++++++++++++++++++++
 gcc/gimple-fold.c     | 83 ++++++++++++++++++++++++++++++++++++++++++---------
 4 files changed, 125 insertions(+), 59 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6696f28..385e78e0 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -148,7 +148,6 @@ static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 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_memchr (location_t, tree, tree, tree, tree);
 static tree fold_builtin_memcmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
@@ -7244,47 +7243,6 @@ fold_builtin_sincos (location_t loc,
 			 fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
-/* Fold function call to builtin memchr.  ARG1, ARG2 and LEN are the
-   arguments to the call, and TYPE is its return type.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, INTEGER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      const char *p1;
-
-      if (TREE_CODE (arg2) != INTEGER_CST
-	  || !tree_fits_uhwi_p (len))
-	return NULL_TREE;
-
-      p1 = c_getstr (arg1);
-      if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
-	{
-	  char c;
-	  const char *r;
-	  tree tem;
-
-	  if (target_char_cast (arg2, &c))
-	    return NULL_TREE;
-
-	  r = (const char *) memchr (p1, c, tree_to_uhwi (len));
-
-	  if (r == NULL)
-	    return build_int_cst (TREE_TYPE (arg1), 0);
-
-	  tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
-	  return fold_convert_loc (loc, type, tem);
-	}
-      return NULL_TREE;
-    }
-}
-
 /* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
    Return NULL_TREE if no simplification can be made.  */
 
@@ -8340,9 +8298,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_MEMCHR:
-      return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
-
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
@@ -9908,3 +9863,17 @@ is_inexpensive_builtin (tree decl)
 
   return false;
 }
+
+/* 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.  */
+
+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;
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8d0acd0..5e83646 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -97,6 +97,7 @@ extern unsigned HOST_WIDE_INT target_percent;
 extern char target_percent_s[3];
 extern char target_percent_c[3];
 extern char target_percent_s_newline[4];
+extern bool target_char_cst_p (tree t, char *p);
 
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 993769c..d84af48 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "fold-const-call.h"
 #include "case-cfn-macros.h"
 #include "tm.h" /* For C[LT]Z_DEFINED_AT_ZERO.  */
+#include "builtins.h"
 
 /* Functions that test for certain constant types, abstracting away the
    decision about whether to check for overflow.  */
@@ -1463,6 +1464,46 @@ fold_const_call_1 (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
       return NULL_TREE;
     }
 
+  switch (fn)
+    {
+    case CFN_BUILT_IN_MEMCHR:
+      {
+	char c;
+	if (integer_zerop (arg2))
+	  return build_int_cst (type, 0);
+
+	if (!tree_fits_uhwi_p (arg2) || !target_char_cst_p (arg1, &c))
+	  return NULL_TREE;
+
+	unsigned HOST_WIDE_INT length = tree_to_uhwi (arg2);
+	unsigned HOST_WIDE_INT searched_length = length;
+
+	const char *p1;
+	unsigned HOST_WIDE_INT string_length;
+	if ((p1 = c_getstr (arg0, -1, &string_length)))
+	  searched_length = MIN (searched_length, string_length);
+	else
+	  p1 = c_getstr (arg1, length);
+
+	if (p1)
+	  {
+	    const char *r = (const char *)memchr (p1, c, searched_length);
+	    if (r == NULL)
+	      {
+		/* In situations like memchr ("abc", 'x', 10) we don't know.  */
+		if (length > searched_length)
+		  return NULL_TREE;
+
+		return build_int_cst (type, 0);
+	      }
+	  }
+
+	break;
+      }
+    default:
+      break;
+    }
+
   return NULL_TREE;
 }
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 62bd593..d3efd00 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,20 +57,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "fold-const-call.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
@@ -1967,6 +1953,73 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
   return false;
 }
 
+/* Fold a call to the memchr pointed by GSI iterator.  */
+
+static bool
+gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    {
+      replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+      return true;
+    }
+
+  char c;
+  if (TREE_CODE (arg2) != INTEGER_CST
+      || !tree_fits_uhwi_p (len)
+      || !target_char_cst_p (arg2, &c))
+    return false;
+
+  unsigned HOST_WIDE_INT length = tree_to_uhwi (len);
+  unsigned HOST_WIDE_INT searched_length = length;
+
+  const char *p1;
+  if ((p1 = c_getstr (arg1)))
+    searched_length = MIN (searched_length, strlen (p1) + 1);
+  else
+    p1 = c_getstr (arg1, length);
+
+  if (p1)
+    {
+      const char *r = (const char *)memchr (p1, c, searched_length);
+      if (r == NULL)
+	{
+	  /* In situations like memchr ("abc", 'x', 10) we don't know.  */
+	  if (length > searched_length)
+	    return false;
+
+	  replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+	  return true;
+	}
+      else
+	{
+	  unsigned HOST_WIDE_INT offset = r - p1;
+	  gimple_seq stmts = NULL;
+	  if (lhs != NULL_TREE)
+	    {
+	      tree offset_cst = build_int_cst (TREE_TYPE (len), offset);
+	      gassign *stmt = gimple_build_assign (lhs, POINTER_PLUS_EXPR,
+						   arg1, offset_cst);
+	      gimple_seq_add_stmt_without_update (&stmts, stmt);
+	    }
+	  else
+	    gimple_seq_add_stmt_without_update (&stmts,
+						gimple_build_nop ());
+
+	  gsi_replace_with_seq_vops (gsi, stmts);
+	  return true;
+	}
+    }
+
+  return false;
+}
 
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
@@ -3194,6 +3247,8 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_STRNCMP:
     case BUILT_IN_STRNCASECMP:
       return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_MEMCHR:
+      return gimple_fold_builtin_memchr (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Test folding of str{n}{case}cmp and memchr (version 3)
  2016-10-12  8:35         ` Richard Biener
@ 2016-10-12 13:20           ` Martin Liška
  2016-10-13 15:27             ` [PATCH] Test folding of str{n}{case}cmp and memchr (simplified version 4) Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-12 13:20 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 437 bytes --]

On 10/12/2016 10:34 AM, Richard Biener wrote:
> On Tue, Oct 11, 2016 at 11:38 AM, Martin Liška <mliska@suse.cz> wrote:
>> Third iteration of tests, where I added both GENERIC and GIMPLE folding
>> tests.
> 
> They should work already with -O1?

Yes, they work. Sending new version where I also added few situations
that trigger an undefined behavior (and should not be folded).

Martin

> 
> Otherwise ok.
> 
> Richard.
> 
>> Martin


[-- Attachment #2: 0005-Test-folding-of-str-n-case-cmp-and-memchr-v4.patch --]
[-- Type: text/x-patch, Size: 9060 bytes --]

From bae7f32a3f602d111ac67019c6cd6dd8efacae00 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 16 Aug 2016 15:56:01 +0200
Subject: [PATCH 5/5] Test folding of str{n}{case}cmp and memchr

gcc/testsuite/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/tree-ssa/builtins-folding-generic.c: New test.
	* gcc.dg/tree-ssa/builtins-folding-gimple.c: Likewise.
	* gcc.dg/tree-ssa/builtins-folding-gimple-ub.c: Likewise.
---
 .../gcc.dg/tree-ssa/builtins-folding-generic.c     |  76 +++++++++++
 .../gcc.dg/tree-ssa/builtins-folding-gimple-ub.c   |  23 ++++
 .../gcc.dg/tree-ssa/builtins-folding-gimple.c      | 140 +++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
new file mode 100644
index 0000000..175feff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
@@ -0,0 +1,76 @@
+/* { dg-do run } */
+/* { dg-options "-O1 -fdump-tree-original" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer2, foo1);
+
+  /* MEMCHR.  */
+  if (__builtin_memchr ("hello world", 'x', 11))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'x', 0) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'w', 2))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'd', 10))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", '\0', 11))
+    __builtin_abort ();
+
+  /* STRCMP.  */
+  if (__builtin_strcmp ("hello", "aaaaa") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("", "aaaaa") >= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("ab", "ba") >= 0)
+    __builtin_abort ();
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("ab", "ba", 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+
+  /* STRCASECMP.  */
+  if (__builtin_strcasecmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strcmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strcasecmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncasecmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "original" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c
new file mode 100644
index 0000000..df0ede2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  /* MEMCHR.  */
+  if (__builtin_memchr ("", 'x', 1000)) /* Not folded away.  */
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'x', 1000)) /* Not folded away.  */
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memchr" 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c
new file mode 100644
index 0000000..4cac93a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c
@@ -0,0 +1,140 @@
+/* { dg-do run } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer2, foo1);
+
+  char x = 'x';
+  char o = 'o';
+  char w = 'w';
+  char d = 'd';
+  char e = 'e';
+  char null = '\0';
+
+  int zero = 0;
+  int one = 0;
+
+  /* MEMCHR.  */
+  if (__builtin_memchr (foo1, x, 11))
+    __builtin_abort ();
+  if (__builtin_memchr (buffer1, x, zero) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, o, 11) != foo1 + 4)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, w, 2))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1 + 5, o, 6) != foo1 + 7)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, d, 11) != foo1 + 10)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, d, 10))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, null, 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, null, 12) != foo1 + 11)
+    __builtin_abort ();
+
+  __builtin_memchr (foo1, x, 11);
+  __builtin_memchr (buffer1, x, zero);
+  __builtin_memchr (foo1, w, 2);
+  __builtin_memchr (foo1, e, 5);
+
+  const char *aaaaa = "aaaaa";
+  const char *hello = "hello";
+  const char *empty = "";
+  const char *ab = "ab";
+  const char *ba = "ba";
+  const char *aac = "aac";
+  const char *aab = "aab";
+
+  /* STRCMP.  */
+  if (__builtin_strcmp (hello, aaaaa) <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (aaaaa, aaaaa) != 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (aaaaa, empty) <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (empty, aaaaa) >= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (ab, ba) >= 0)
+    __builtin_abort ();
+
+  __builtin_strcmp (hello, aaaaa);
+  __builtin_strcmp (aaaaa, aaaaa);
+  __builtin_strcmp (aaaaa, empty);
+  __builtin_strcmp (empty, aaaaa);
+  __builtin_strcmp (ab, ba);
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp (hello, aaaaa, zero) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (aaaaa, aaaaa, 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (aaaaa, empty, 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (empty, aaaaa, 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (ab, ba, 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (aab, aac, 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (buffer1, buffer2, 1) != 0)
+    __builtin_abort (); /* not folded away */
+
+  __builtin_strncmp (hello, aaaaa, zero);
+  __builtin_strncmp (aaaaa, aaaaa, 100);
+  __builtin_strncmp (aaaaa, empty, 100);
+  __builtin_strncmp (empty, aaaaa, 100);
+  __builtin_strncmp (ab, ba, 1);
+  __builtin_strncmp (aab, aac, 2);
+  __builtin_strncmp (buffer1, buffer2, zero);
+  __builtin_strncmp (buffer1, buffer2, one);
+  __builtin_strncmp (empty, buffer2, 1);
+  __builtin_strncmp (buffer1, empty, 1);
+
+  /* STRCASECMP.  */
+  if (__builtin_strcasecmp (aaaaa, aaaaa) != 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp (aaaaa, empty) <= 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp (empty, aaaaa) >= 0)
+    __builtin_abort ();
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp (hello, aaaaa, zero) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (aaaaa, aaaaa, 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (aaaaa, empty, 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (empty, aaaaa, 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (aab, aac, 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (ab, ba, 1) >= 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 1) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 100) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strcmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strcasecmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncasecmp" 3 "optimized" } } */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [RFC] Possible folding opportunities for string built-ins
  2016-08-17  6:55 [PATCH 0/3] Better folding of 2 string builtin-ins marxin
                   ` (2 preceding siblings ...)
  2016-08-17  6:55 ` [PATCH 1/3] Fold BUILT_IN_STRNCASECMP marxin
@ 2016-10-12 13:48 ` Martin Liška
  2016-10-12 15:55   ` Joseph Myers
  2016-10-13  8:38   ` Richard Biener
  3 siblings, 2 replies; 45+ messages in thread
From: Martin Liška @ 2016-10-12 13:48 UTC (permalink / raw)
  To: gcc-patches; +Cc: Richard Biener, Jan Hubicka, Jakub Jelinek

Hi.

As you probably mentioned, simple folding improvement has grown to multiple patches
and multiple iterations. Apart from that, I also noticed that we do not do the best
for couple of cases and I would like to have a feedback if it worth to improve or not?

$ cat /tmp/string-folding-missing.c 
const char global_1[4] = {'a', 'b', 'c', 'd' };
const char global_2[6] = "abcdefghijk";

int main()
{
  const char local1[] = "asdfasdfasdf";

  /* Case 1 */
  __builtin_memchr (global_1, 'c', 5);

  /* Case 2 */
  __builtin_memchr (global_2, 'c', 5);

  /* Case 3 */
  __builtin_memchr (local1, 'a', 5);

  return 0;
}

Cases:
1) Currently, calling c_getstr (which calls string_constant) can't handle CONSTRUCTOR. Potential
solution can be to create on demand STRING_CST, however as string_constant is called multiple times,
it can be overkill.
2) /tmp/xxxxx.c:2:26: warning: initializer-string for array of chars is too long
 const char global_2[6] = "abcdefghijk";
Here I'm not sure whether one can consider global_2 == "abcdef" (w/o trailing zero char) or not?
If so, adding new output argument (string_length) to string_constant can be solution.
3) Currently, ctor_for_folding return error_mark_node for local variables. I'm wondering whether returning
DECL_INITIAL for these would be doable? Will it make any issue for LTO?

Last question is whether one can aggressively fold strcasecmp in a host compiler? Or are there any situations
where results depends on locale?

Thanks for thoughts.
Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [RFC] Possible folding opportunities for string built-ins
  2016-10-12 13:48 ` [RFC] Possible folding opportunities for string built-ins Martin Liška
@ 2016-10-12 15:55   ` Joseph Myers
  2016-10-12 19:45     ` Jim Wilson
  2016-10-13  8:38   ` Richard Biener
  1 sibling, 1 reply; 45+ messages in thread
From: Joseph Myers @ 2016-10-12 15:55 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, Richard Biener, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 383 bytes --]

On Wed, 12 Oct 2016, Martin Liška wrote:

> Last question is whether one can aggressively fold strcasecmp in a host 
> compiler? Or are there any situations where results depends on locale?

There are the usual issues with Turkish locales having the uppercase 
version of 'i' being 'İ' and the lowercase version of 'I' being 'ı'.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [RFC] Possible folding opportunities for string built-ins
  2016-10-12 15:55   ` Joseph Myers
@ 2016-10-12 19:45     ` Jim Wilson
  0 siblings, 0 replies; 45+ messages in thread
From: Jim Wilson @ 2016-10-12 19:45 UTC (permalink / raw)
  To: Joseph Myers, Martin Liška
  Cc: gcc-patches, Richard Biener, Jan Hubicka, Jakub Jelinek

On 10/12/2016 08:55 AM, Joseph Myers wrote:
> On Wed, 12 Oct 2016, Martin Liška wrote:
>
>> Last question is whether one can aggressively fold strcasecmp in a host
>> compiler? Or are there any situations where results depends on locale?
>
> There are the usual issues with Turkish locales having the uppercase
> version of 'i' being 'İ' and the lowercase version of 'I' being 'ı'.

See for instance
   https://en.wikipedia.org/wiki/Dotted_and_dotless_I

Jim

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [RFC] Possible folding opportunities for string built-ins
  2016-10-12 13:48 ` [RFC] Possible folding opportunities for string built-ins Martin Liška
  2016-10-12 15:55   ` Joseph Myers
@ 2016-10-13  8:38   ` Richard Biener
  1 sibling, 0 replies; 45+ messages in thread
From: Richard Biener @ 2016-10-13  8:38 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches, Jan Hubicka, Jakub Jelinek

On Wed, Oct 12, 2016 at 3:48 PM, Martin Liška <mliska@suse.cz> wrote:
> Hi.
>
> As you probably mentioned, simple folding improvement has grown to multiple patches
> and multiple iterations. Apart from that, I also noticed that we do not do the best
> for couple of cases and I would like to have a feedback if it worth to improve or not?
>
> $ cat /tmp/string-folding-missing.c
> const char global_1[4] = {'a', 'b', 'c', 'd' };
> const char global_2[6] = "abcdefghijk";
>
> int main()
> {
>   const char local1[] = "asdfasdfasdf";
>
>   /* Case 1 */
>   __builtin_memchr (global_1, 'c', 5);
>
>   /* Case 2 */
>   __builtin_memchr (global_2, 'c', 5);
>
>   /* Case 3 */
>   __builtin_memchr (local1, 'a', 5);
>
>   return 0;
> }
>
> Cases:
> 1) Currently, calling c_getstr (which calls string_constant) can't handle CONSTRUCTOR. Potential
> solution can be to create on demand STRING_CST, however as string_constant is called multiple times,
> it can be overkill.

I believe somewhere during GENERICIZation / GIMPLIFICATION we should
simply turn those
COSNTRUCTORs into STRING_CSTs ... (probably not in string_constant
itself as that would be somewhat
gross of a place).

> 2) /tmp/xxxxx.c:2:26: warning: initializer-string for array of chars is too long
>  const char global_2[6] = "abcdefghijk";
> Here I'm not sure whether one can consider global_2 == "abcdef" (w/o trailing zero char) or not?
> If so, adding new output argument (string_length) to string_constant can be solution.

Likewise if we are able to warn the FE should be able to truncate the
STRING_CST itself.  The
question is still whether a non-NULL terminated string should be
constant folded (it looks like
the STRING_PTR in a STRING_CST is always '\0' terminated).

> 3) Currently, ctor_for_folding return error_mark_node for local variables. I'm wondering whether returning
> DECL_INITIAL for these would be doable? Will it make any issue for LTO?

They do not prevail (ok, you might see this during GENERIC folding).
They get lowered to runtime
initialization either from a constant or a CONST_DECL (with
DECL_INITIAL).  For those CONST_DECLs
we should return a ctor_for_folding.

Richard.

> Last question is whether one can aggressively fold strcasecmp in a host compiler? Or are there any situations
> where results depends on locale?
>
> Thanks for thoughts.
> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Check \0-termination of string in c_getstr (simplified version)
  2016-10-12 13:14           ` Martin Liška
@ 2016-10-13 15:24             ` Martin Liška
  2016-10-14  9:38               ` Richard Biener
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-13 15:24 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 393 bytes --]

Hello.

After receiving feedback from Richi and Wilco Dijkstra, I decided to fully not
support not null-terminated strings. It brings more complications and the code has started
to be overengineered. Thus c_getstr accepts only such strings and as a bonus it returns
length of a string.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0001-Support-only-0-terminated-string-in-c_getstr-and-ret-simplified.patch --]
[-- Type: text/x-patch, Size: 2934 bytes --]

From bee44f0dedc86b1c354e21dd87dad6313147dcc3 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 13 Oct 2016 10:20:12 +0200
Subject: [PATCH 1/4] Support only \0-terminated string in c_getstr and return
 strlen

gcc/ChangeLog:

2016-10-13  Martin Liska  <mliska@suse.cz>

	* fold-const.c (c_getstr): Support of properly \0-terminated
	string constants.  New argument is added.
	* fold-const.h: New argument is added.
---
 gcc/fold-const.c | 38 +++++++++++++++++++++++++++++---------
 gcc/fold-const.h |  2 +-
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 02aa484..57a9243 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -14440,24 +14440,44 @@ fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE_INT off)
 }
 
 /* Return a char pointer for a C string if it is a string constant
-   or sum of string constant and integer constant.  */
+   or sum of string constant and integer constant.  We only support
+   string constants properly terminated with '\0' character.
+   If STRLEN is a valid pointer, length (including terminating character)
+   of returned string is stored to the argument.  */
 
 const char *
-c_getstr (tree src)
+c_getstr (tree src, unsigned HOST_WIDE_INT *strlen)
 {
   tree offset_node;
 
+  if (strlen)
+    *strlen = 0;
+
   src = string_constant (src, &offset_node);
   if (src == 0)
-    return 0;
+    return NULL;
 
-  if (offset_node == 0)
-    return TREE_STRING_POINTER (src);
-  else if (!tree_fits_uhwi_p (offset_node)
-	   || compare_tree_int (offset_node, TREE_STRING_LENGTH (src) - 1) > 0)
-    return 0;
+  unsigned HOST_WIDE_INT offset = 0;
+  if (offset_node != NULL_TREE)
+    {
+      if (!tree_fits_uhwi_p (offset_node))
+	return NULL;
+      else
+	offset = tree_to_uhwi (offset_node);
+    }
+
+  unsigned HOST_WIDE_INT string_length = TREE_STRING_LENGTH (src);
+  const char *string = TREE_STRING_POINTER (src);
+
+  /* Support only properly null-terminated strings.  */
+  if (string_length == 0
+      || string[string_length - 1] != '\0'
+      || offset > string_length)
+    return NULL;
 
-  return TREE_STRING_POINTER (src) + tree_to_uhwi (offset_node);
+  if (strlen)
+    *strlen = string_length - offset;
+  return string + offset;
 }
 
 #if CHECKING_P
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index 637e46b..bc22c88 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -182,7 +182,7 @@ 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);
+extern const char *c_getstr (tree, unsigned HOST_WIDE_INT *strlen = NULL);
 
 /* Return OFF converted to a pointer offset type suitable as offset for
    POINTER_PLUS_EXPR.  Use location LOC for this conversion.  */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Fold __builtin_str{n}{case}cmp functions (simplified version 4)
  2016-10-12 13:17           ` Martin Liška
@ 2016-10-13 15:25             ` Martin Liška
  2016-10-14 11:55               ` Richard Biener
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-13 15:25 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 174 bytes --]

Simplified version that just supports only null-terminated strings.
Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0002-Fold-__builtin_str-n-case-cmp-functions-simplified.patch --]
[-- Type: text/x-patch, Size: 16100 bytes --]

From 41c49024a02cff43774903206ad77b2ae161e81a Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 13 Oct 2016 10:25:25 +0200
Subject: [PATCH 2/4] Fold __builtin_str{n}{case}cmp functions

gcc/ChangeLog:

2016-10-13  Martin Liska  <mliska@suse.cz>

	* builtins.c (fold_builtin_strcmp): Remove function.
	(fold_builtin_strncmp): Likewise.
	(fold_builtin_2): Remove call of the function.
	(fold_builtin_3): Likewise.
	* fold-const-call.c (fold_const_call): Add constant folding
	for CFN_BUILT_IN_STRCASECMP and CFN_BUILT_IN_STRNCASECMP.
	* fold-const-call.h (build_cmp_result): Declare the function.
	* gimple-fold.c (gimple_load_first_char): New function.
	(gimple_fold_builtin_string_compare): Likewise.
	(gimple_fold_builtin): Call the function.
---
 gcc/builtins.c        | 138 ------------------------------------
 gcc/fold-const-call.c |  45 +++++++++---
 gcc/fold-const-call.h |   1 +
 gcc/gimple-fold.c     | 189 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 226 insertions(+), 147 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 43a9db0..ed5a635 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -150,8 +150,6 @@ static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_signbit (tree, rtx);
 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_strncmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
 static tree fold_builtin_isdigit (location_t, tree);
@@ -7331,136 +7329,6 @@ fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
   return NULL_TREE;
 }
 
-/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE))
-    return NULL_TREE;
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return integer_zero_node;
-
-  /* If the second arg is "", return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp
-	= fold_convert_loc (loc, integer_type_node,
-			    build1 (INDIRECT_REF, cst_uchar_node,
-				    fold_convert_loc (loc,
-						      cst_uchar_ptr_node,
-						      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* If the LEN parameter is zero, return zero.  */
-  if (integer_zerop (len))
-    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
-			      arg1, arg2);
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
-
-  /* If the second arg is "", and the length is greater than zero,
-     return *(const unsigned char*)arg1.  */
-  const char *p2 = c_getstr (arg2);
-  if (p2 && *p2 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-			       build1 (INDIRECT_REF, cst_uchar_node,
-				       fold_convert_loc (loc,
-							 cst_uchar_ptr_node,
-							 arg1)));
-    }
-
-  /* If the first arg is "", and the length is greater than zero,
-     return -*(const unsigned char*)arg2.  */
-  const char *p1 = c_getstr (arg1);
-  if (p1 && *p1 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  /* If len parameter is one, return an expression corresponding to
-     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree ind1 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg1)));
-      tree ind2 = fold_convert_loc (loc, integer_type_node,
-				    build1 (INDIRECT_REF, cst_uchar_node,
-					    fold_convert_loc (loc,
-							      cst_uchar_ptr_node,
-							      arg2)));
-      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold a call to builtin isascii with argument ARG.  */
 
 static tree
@@ -8389,9 +8257,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     case BUILT_IN_STRCSPN:
       return fold_builtin_strcspn (loc, arg0, arg1);
 
-    case BUILT_IN_STRCMP:
-      return fold_builtin_strcmp (loc, arg0, arg1);
-
     case BUILT_IN_STRPBRK:
       return fold_builtin_strpbrk (loc, arg0, arg1, type);
 
@@ -8473,9 +8338,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_STRNCMP:
-      return fold_builtin_strncmp (loc, arg0, arg1, arg2);
-
     case BUILT_IN_MEMCHR:
       return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
 
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 2bbc887..f67b245 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -69,7 +69,7 @@ host_size_t_cst_p (tree t, size_t *size_out)
    "equal" and > 0 means "more".  Canonicalize it to -1, 0 or 1 and
    return it in type TYPE.  */
 
-static inline tree
+tree
 build_cmp_result (tree type, int res)
 {
   return build_int_cst (type, res < 0 ? -1 : res > 0 ? 1 : 0);
@@ -1397,6 +1397,15 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
 	return build_cmp_result (type, strcmp (p0, p1));
       return NULL_TREE;
 
+    case CFN_BUILT_IN_STRCASECMP:
+      if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
+	{
+	  int r = strcmp (p0, p1);
+	  if (r == 0)
+	    return build_cmp_result (type, r);
+	}
+      return NULL_TREE;
+
     default:
       return fold_const_call_1 (fn, type, arg0, arg1);
     }
@@ -1464,16 +1473,36 @@ tree
 fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
 {
   const char *p0, *p1;
-  size_t s2;
+  size_t s2 = 0;
   switch (fn)
     {
     case CFN_BUILT_IN_STRNCMP:
-      if ((p0 = c_getstr (arg0))
-	  && (p1 = c_getstr (arg1))
-	  && host_size_t_cst_p (arg2, &s2))
-	return build_int_cst (type, strncmp (p0, p1, s2));
-      return NULL_TREE;
-
+      {
+	bool const_size_p = host_size_t_cst_p (arg2, &s2);
+	if (const_size_p && s2 == 0
+	    && !TREE_SIDE_EFFECTS (arg0)
+	    && !TREE_SIDE_EFFECTS (arg1))
+	  return build_int_cst (type, 0);
+	else if (const_size_p
+		 && (p0 = c_getstr (arg0))
+		 && (p1 = c_getstr (arg1)))
+	  return build_int_cst (type, strncmp (p0, p1, s2));
+	return NULL_TREE;
+      }
+    case CFN_BUILT_IN_STRNCASECMP:
+      {
+	bool const_size_p = host_size_t_cst_p (arg2, &s2);
+	if (const_size_p && s2 == 0
+	    && !TREE_SIDE_EFFECTS (arg0)
+	    && !TREE_SIDE_EFFECTS (arg1))
+	  return build_int_cst (type, 0);
+	else if (const_size_p
+		 && (p0 = c_getstr (arg0))
+		 && (p1 = c_getstr (arg1))
+		 && strncmp (p0, p1, s2) == 0)
+	  return build_int_cst (type, 0);
+	return NULL_TREE;
+      }
     case CFN_BUILT_IN_BCMP:
     case CFN_BUILT_IN_MEMCMP:
       if ((p0 = c_getstr (arg0))
diff --git a/gcc/fold-const-call.h b/gcc/fold-const-call.h
index 324ecbf..7f61f2e 100644
--- a/gcc/fold-const-call.h
+++ b/gcc/fold-const-call.h
@@ -24,5 +24,6 @@ tree fold_const_call (combined_fn, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree);
 tree fold_const_call (combined_fn, tree, tree, tree, tree);
 tree fold_fma (location_t, tree, tree, tree, tree);
+tree build_cmp_result (tree type, int res);
 
 #endif
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 1836927..f349472 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -55,7 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-low.h"
 #include "ipa-chkp.h"
 #include "tree-cfg.h"
-
+#include "fold-const-call.h"
 
 /* Return true if T is a constant and the value cast to a target char
    can be represented by a host char.
@@ -1786,6 +1786,188 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Build and append gimple statements to STMTS that would load a first
+   character of a memory location identified by STR.  LOC is location
+   of the statement.  */
+
+static tree
+gimple_load_first_char (location_t loc, tree str, gimple_seq *stmts)
+{
+  tree var;
+
+  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+  tree cst_uchar_ptr_node
+    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
+
+  tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str, off0);
+  gassign *stmt = gimple_build_assign (NULL_TREE, temp);
+  var = create_tmp_reg_or_ssa_name (cst_uchar_node, stmt);
+
+  gimple_assign_set_lhs (stmt, var);
+  gimple_seq_add_stmt_without_update (stmts, stmt);
+
+  return var;
+}
+
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */
+
+static bool
+gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree callee = gimple_call_fndecl (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+
+  tree type = integer_type_node;
+  tree str1 = gimple_call_arg (stmt, 0);
+  tree str2 = gimple_call_arg (stmt, 1);
+  tree lhs = gimple_call_lhs (stmt);
+  HOST_WIDE_INT length = -1;
+
+  /* Handle strncmp and strncasecmp functions.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_uhwi_p (len))
+	length = tree_to_uhwi (len);
+    }
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (length == 0)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (str1, str2, 0))
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  const char *p1 = c_getstr (str1);
+  const char *p2 = c_getstr (str2);
+
+  /* For known strings, return an immediate value.  */
+  if (p1 && p2)
+    {
+      int r = 0;
+      bool known_result = false;
+
+      switch (fcode)
+	{
+	case BUILT_IN_STRCMP:
+	  {
+	    r = strcmp (p1, p2);
+	    known_result = true;
+	    break;
+	  }
+	case BUILT_IN_STRNCMP:
+	  {
+	    if (length == -1)
+	      break;
+	    r = strncmp (p1, p2, length);
+	    known_result = true;
+	    break;
+	  }
+	/* Only handleable situation is where the string are equal (result 0),
+	   which is already handled by operand_equal_p case.  */
+	case BUILT_IN_STRCASECMP:
+	  break;
+	case BUILT_IN_STRNCASECMP:
+	  {
+	    if (length == -1)
+	      break;
+	    r = strncmp (p1, p2, length);
+	    if (r == 0)
+	      known_result = true;
+	    break;;
+	  }
+	default:
+	  gcc_unreachable ();
+	}
+
+      if (known_result)
+	{
+	  replace_call_with_value (gsi, build_cmp_result (type, r));
+	  return true;
+	}
+    }
+
+  bool nonzero_length = length >= 1
+    || fcode == BUILT_IN_STRCMP
+    || fcode == BUILT_IN_STRCASECMP;
+
+  location_t loc = gimple_location (stmt);
+
+  /* If the second arg is "", return *(const unsigned char*)arg1.  */
+  if (p2 && *p2 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str1, &stmts);
+      if (lhs)
+	{
+	  stmt = gimple_build_assign (lhs, NOP_EXPR, var);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+	}
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
+  if (p1 && *p1 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str2, &stmts);
+
+      if (lhs)
+	{
+	  tree c = create_tmp_reg_or_ssa_name (integer_type_node);
+	  stmt = gimple_build_assign (c, NOP_EXPR, var);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+	  stmt = gimple_build_assign (lhs, NEGATE_EXPR, c);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+	}
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg2 - *(const unsigned char*)arg1).  */
+  if (fcode == BUILT_IN_STRNCMP && length == 1)
+    {
+      gimple_seq stmts = NULL;
+      tree temp1 = gimple_load_first_char (loc, str1, &stmts);
+      tree temp2 = gimple_load_first_char (loc, str2, &stmts);
+
+      if (lhs)
+	{
+	  tree c1 = create_tmp_reg_or_ssa_name (integer_type_node);
+	  gassign *convert1 = gimple_build_assign (c1, NOP_EXPR, temp1);
+	  gimple_seq_add_stmt_without_update (&stmts, convert1);
+
+	  tree c2 = create_tmp_reg_or_ssa_name (integer_type_node);
+	  gassign *convert2 = gimple_build_assign (c2, NOP_EXPR, temp2);
+	  gimple_seq_add_stmt_without_update (&stmts, convert2);
+
+	  stmt = gimple_build_assign (lhs, MINUS_EXPR, c1, c2);
+	  gimple_seq_add_stmt_without_update (&stmts, stmt);
+	}
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  return false;
+}
+
+
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
    by the builtin will be ignored.  UNLOCKED is true is true if this
@@ -3007,6 +3189,11 @@ 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_STRCMP:
+    case BUILT_IN_STRCASECMP:
+    case BUILT_IN_STRNCMP:
+    case BUILT_IN_STRNCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Fold __builtin_memchr (simplified version 4)
  2016-10-12 13:19             ` Martin Liška
@ 2016-10-13 15:26               ` Martin Liška
  2016-10-14 11:57                 ` Richard Biener
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-13 15:26 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 279 bytes --]

Simplified version that supports only valid null-terminated string constants.
Apart from that, I added checking for constant folding of expressions that
have side effects.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0003-Fold-__builtin_memchr-function-simplified.patch --]
[-- Type: text/x-patch, Size: 8514 bytes --]

From 5028bf5cf23cda31e72a342b821474ed0c3c07b9 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 13 Oct 2016 10:30:56 +0200
Subject: [PATCH 3/4] Fold __builtin_memchr function

gcc/ChangeLog:

2016-10-13  Martin Liska  <mliska@suse.cz>

	* builtins.h(target_char_cst_p): Declare the function.
	* builtins.c (fold_builtin_memchr): Remove.
	(target_char_cst_p): Move the function from gimple-fold.c.
	(fold_builtin_3): Do not call the function.
	* gimple-fold.c (gimple_fold_builtin_memchr): New function.
	(gimple_fold_builtin): Call the function.
	* fold-const-call.c (fold_const_call_1): Handle CFN_BUILT_IN_MEMCHR.
---
 gcc/builtins.c        | 59 ++++++++++-----------------------------
 gcc/builtins.h        |  1 +
 gcc/fold-const-call.c | 31 +++++++++++++++++++++
 gcc/gimple-fold.c     | 77 +++++++++++++++++++++++++++++++++++++++++----------
 4 files changed, 109 insertions(+), 59 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index ed5a635..03d8563 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -148,7 +148,6 @@ static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 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_memchr (location_t, tree, tree, tree, tree);
 static tree fold_builtin_memcmp (location_t, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
@@ -7242,47 +7241,6 @@ fold_builtin_sincos (location_t loc,
 			 fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
-/* Fold function call to builtin memchr.  ARG1, ARG2 and LEN are the
-   arguments to the call, and TYPE is its return type.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, INTEGER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      const char *p1;
-
-      if (TREE_CODE (arg2) != INTEGER_CST
-	  || !tree_fits_uhwi_p (len))
-	return NULL_TREE;
-
-      p1 = c_getstr (arg1);
-      if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
-	{
-	  char c;
-	  const char *r;
-	  tree tem;
-
-	  if (target_char_cast (arg2, &c))
-	    return NULL_TREE;
-
-	  r = (const char *) memchr (p1, c, tree_to_uhwi (len));
-
-	  if (r == NULL)
-	    return build_int_cst (TREE_TYPE (arg1), 0);
-
-	  tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
-	  return fold_convert_loc (loc, type, tem);
-	}
-      return NULL_TREE;
-    }
-}
-
 /* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
    Return NULL_TREE if no simplification can be made.  */
 
@@ -8338,9 +8296,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_MEMCHR:
-      return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
-
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
@@ -9906,3 +9861,17 @@ is_inexpensive_builtin (tree decl)
 
   return false;
 }
+
+/* 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.  */
+
+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;
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8d0acd0..5e83646 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -97,6 +97,7 @@ extern unsigned HOST_WIDE_INT target_percent;
 extern char target_percent_s[3];
 extern char target_percent_c[3];
 extern char target_percent_s_newline[4];
+extern bool target_char_cst_p (tree t, char *p);
 
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index f67b245..05a15f9 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "fold-const-call.h"
 #include "case-cfn-macros.h"
 #include "tm.h" /* For C[LT]Z_DEFINED_AT_ZERO.  */
+#include "builtins.h"
 
 /* Functions that test for certain constant types, abstracting away the
    decision about whether to check for overflow.  */
@@ -1463,6 +1464,36 @@ fold_const_call_1 (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
       return NULL_TREE;
     }
 
+  switch (fn)
+    {
+    case CFN_BUILT_IN_MEMCHR:
+      {
+	char c;
+	if (integer_zerop (arg2)
+	    && !TREE_SIDE_EFFECTS (arg0)
+	    && !TREE_SIDE_EFFECTS (arg1))
+	  return build_int_cst (type, 0);
+
+	if (!tree_fits_uhwi_p (arg2) || !target_char_cst_p (arg1, &c))
+	  return NULL_TREE;
+
+	unsigned HOST_WIDE_INT length = tree_to_uhwi (arg2);
+	unsigned HOST_WIDE_INT string_length;
+	const char *p1 = c_getstr (arg0, &string_length);
+	if (p1)
+	  {
+	    const char *r
+	      = (const char *)memchr (p1, c, MIN (length, string_length));
+	    if (r == NULL && length <= string_length)
+	      return build_int_cst (type, 0);
+	  }
+
+	break;
+      }
+    default:
+      break;
+    }
+
   return NULL_TREE;
 }
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index f349472..5d46405 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,20 +57,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "fold-const-call.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
@@ -1967,6 +1953,67 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
   return false;
 }
 
+/* Fold a call to the memchr pointed by GSI iterator.  */
+
+static bool
+gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    {
+      replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+      return true;
+    }
+
+  char c;
+  if (TREE_CODE (arg2) != INTEGER_CST
+      || !tree_fits_uhwi_p (len)
+      || !target_char_cst_p (arg2, &c))
+    return false;
+
+  unsigned HOST_WIDE_INT length = tree_to_uhwi (len);
+  unsigned HOST_WIDE_INT string_length;
+  const char *p1 = c_getstr (arg1, &string_length);
+
+  if (p1)
+    {
+      const char *r = (const char *)memchr (p1, c, MIN (length, string_length));
+      if (r == NULL)
+	{
+	  if (length <= string_length)
+	    {
+	      replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+	      return true;
+	    }
+	}
+      else
+	{
+	  unsigned HOST_WIDE_INT offset = r - p1;
+	  gimple_seq stmts = NULL;
+	  if (lhs != NULL_TREE)
+	    {
+	      tree offset_cst = build_int_cst (TREE_TYPE (len), offset);
+	      gassign *stmt = gimple_build_assign (lhs, POINTER_PLUS_EXPR,
+						   arg1, offset_cst);
+	      gimple_seq_add_stmt_without_update (&stmts, stmt);
+	    }
+	  else
+	    gimple_seq_add_stmt_without_update (&stmts,
+						gimple_build_nop ());
+
+	  gsi_replace_with_seq_vops (gsi, stmts);
+	  return true;
+	}
+    }
+
+  return false;
+}
 
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
    to the call.  IGNORE is true if the value returned
@@ -3194,6 +3241,8 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_STRNCMP:
     case BUILT_IN_STRNCASECMP:
       return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_MEMCHR:
+      return gimple_fold_builtin_memchr (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
 					gimple_call_arg (stmt, 1), false);
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Test folding of str{n}{case}cmp and memchr (simplified version 4)
  2016-10-12 13:20           ` Martin Liška
@ 2016-10-13 15:27             ` Martin Liška
  2016-10-14 11:58               ` Richard Biener
  0 siblings, 1 reply; 45+ messages in thread
From: Martin Liška @ 2016-10-13 15:27 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 174 bytes --]

Simplified version of tests, where I added tests for side effects.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0004-Test-folding-of-str-n-case-cmp-and-memchr-simplified.patch --]
[-- Type: text/x-patch, Size: 9657 bytes --]

From 83da10e2bd4f4e36028ca33d7d3a0472e8b46d7a Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 16 Aug 2016 15:56:01 +0200
Subject: [PATCH 4/4] Test folding of str{n}{case}cmp and memchr

gcc/testsuite/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/tree-ssa/builtins-folding-generic.c: New test.
	* gcc.dg/tree-ssa/builtins-folding-gimple.c: Likewise.
	* gcc.dg/tree-ssa/builtins-folding-gimple-ub.c: Likewise.
---
 .../gcc.dg/tree-ssa/builtins-folding-generic.c     |  76 ++++++++++
 .../gcc.dg/tree-ssa/builtins-folding-gimple-ub.c   |  23 +++
 .../gcc.dg/tree-ssa/builtins-folding-gimple.c      | 161 +++++++++++++++++++++
 3 files changed, 260 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
new file mode 100644
index 0000000..175feff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-generic.c
@@ -0,0 +1,76 @@
+/* { dg-do run } */
+/* { dg-options "-O1 -fdump-tree-original" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer2, foo1);
+
+  /* MEMCHR.  */
+  if (__builtin_memchr ("hello world", 'x', 11))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'x', 0) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'w', 2))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", 'd', 10))
+    __builtin_abort ();
+  if (__builtin_memchr ("hello world", '\0', 11))
+    __builtin_abort ();
+
+  /* STRCMP.  */
+  if (__builtin_strcmp ("hello", "aaaaa") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("aaaaa", "") <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("", "aaaaa") >= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp ("ab", "ba") >= 0)
+    __builtin_abort ();
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aaaaa", "", 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("", "aaaaa", 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("ab", "ba", 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+
+  /* STRCASECMP.  */
+  if (__builtin_strcasecmp ("aaaaa", "aaaaa") != 0)
+    __builtin_abort ();
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp ("hello", "aaaaa", 0) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aaaaa", "aaaaa", 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp ("aab", "aac", 2) != 0)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strcmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strcasecmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncasecmp" "original" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "original" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c
new file mode 100644
index 0000000..df0ede2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple-ub.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  /* MEMCHR.  */
+  if (__builtin_memchr ("", 'x', 1000)) /* Not folded away.  */
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, 'x', 1000)) /* Not folded away.  */
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memchr" 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c
new file mode 100644
index 0000000..283bd1c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtins-folding-gimple.c
@@ -0,0 +1,161 @@
+/* { dg-do run } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+char *buffer1;
+char *buffer2;
+
+#define SIZE 1000
+
+int
+main (void)
+{
+  const char* const foo1 = "hello world";
+
+  buffer1 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer1, foo1);
+  buffer2 = __builtin_malloc (SIZE);
+  __builtin_strcpy (buffer2, foo1);
+
+  char x = 'x';
+  char o = 'o';
+  char w = 'w';
+  char d = 'd';
+  char e = 'e';
+  char null = '\0';
+
+  int zero = 0;
+  int one = 0;
+
+  /* MEMCHR.  */
+  if (__builtin_memchr (foo1, x, 11))
+    __builtin_abort ();
+  if (__builtin_memchr (buffer1, x, zero) != 0)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, o, 11) != foo1 + 4)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, w, 2))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1 + 5, o, 6) != foo1 + 7)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, d, 11) != foo1 + 10)
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, d, 10))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, null, 11))
+    __builtin_abort ();
+  if (__builtin_memchr (foo1, null, 12) != foo1 + 11)
+    __builtin_abort ();
+
+  __builtin_memchr (foo1, x, 11);
+  __builtin_memchr (buffer1, x, zero);
+  __builtin_memchr (foo1, w, 2);
+  __builtin_memchr (foo1, e, 5);
+
+  /* MEMCHR with side effects.  */
+  const char *const s1 = "hello world";
+  const char *s2 = s1;
+  if (__builtin_memchr (++s2, 'x', 0) != 0 || s2 != s1+1)
+    __builtin_abort();
+
+  char c = 'x';
+  if (__builtin_memchr (s2, ++c, 0) != 0 || c != 'y')
+    __builtin_abort();
+
+  const char *aaaaa = "aaaaa";
+  const char *hello = "hello";
+  const char *empty = "";
+  const char *ab = "ab";
+  const char *ba = "ba";
+  const char *aac = "aac";
+  const char *aab = "aab";
+
+  /* STRCMP.  */
+  if (__builtin_strcmp (hello, aaaaa) <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (aaaaa, aaaaa) != 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (aaaaa, empty) <= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (empty, aaaaa) >= 0)
+    __builtin_abort ();
+  if (__builtin_strcmp (ab, ba) >= 0)
+    __builtin_abort ();
+
+  __builtin_strcmp (hello, aaaaa);
+  __builtin_strcmp (aaaaa, aaaaa);
+  __builtin_strcmp (aaaaa, empty);
+  __builtin_strcmp (empty, aaaaa);
+  __builtin_strcmp (ab, ba);
+
+  /* STRNCMP.  */
+  if (__builtin_strncmp (hello, aaaaa, zero) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (aaaaa, aaaaa, 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (aaaaa, empty, 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (empty, aaaaa, 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (ab, ba, 1) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (aab, aac, 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncmp (buffer1, buffer2, 1) != 0)
+    __builtin_abort (); /* not folded away */
+
+  __builtin_strncmp (hello, aaaaa, zero);
+  __builtin_strncmp (aaaaa, aaaaa, 100);
+  __builtin_strncmp (aaaaa, empty, 100);
+  __builtin_strncmp (empty, aaaaa, 100);
+  __builtin_strncmp (ab, ba, 1);
+  __builtin_strncmp (aab, aac, 2);
+  __builtin_strncmp (buffer1, buffer2, zero);
+  __builtin_strncmp (buffer1, buffer2, one);
+  __builtin_strncmp (empty, buffer2, 1);
+  __builtin_strncmp (buffer1, empty, 1);
+
+  s2 = s1;
+  const char *s3 = s1+4;
+  if (__builtin_strncmp (++s2, ++s3+2, 0) != 0 || s2 != s1+1 || s3 != s1+5)
+    __builtin_abort();
+
+  /* STRCASECMP.  */
+  if (__builtin_strcasecmp (aaaaa, aaaaa) != 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp (aaaaa, empty) <= 0)
+    __builtin_abort ();
+  if (__builtin_strcasecmp (empty, aaaaa) >= 0)
+    __builtin_abort ();
+
+  /* STRNCASECMP.  */
+  if (__builtin_strncasecmp (hello, aaaaa, zero) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (aaaaa, aaaaa, 100) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (aaaaa, empty, 100) <= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (empty, aaaaa, 100) >= 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (aab, aac, 2) != 0)
+    __builtin_abort ();
+  if (__builtin_strncasecmp (ab, ba, 1) >= 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 1) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+  if (__builtin_strncasecmp (buffer1, buffer2, 100) != 0) /* not folded away */
+    __builtin_abort (); /* not folded away */
+
+  /* STRNCASECMP with side effects.  */
+  s2 = s1;
+  s3 = s1+4;
+  if (__builtin_strncasecmp (++s2, ++s3+2, 0) != 0 || s2 != s1+1 || s3 != s1+5)
+    __builtin_abort();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strcmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strcasecmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strncmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_memchr" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncasecmp" 3 "optimized" } } */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Check \0-termination of string in c_getstr (simplified version)
  2016-10-13 15:24             ` [PATCH] Check \0-termination of string in c_getstr (simplified version) Martin Liška
@ 2016-10-14  9:38               ` Richard Biener
  2016-10-14 11:10                 ` Martin Liška
  0 siblings, 1 reply; 45+ messages in thread
From: Richard Biener @ 2016-10-14  9:38 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Thu, Oct 13, 2016 at 5:23 PM, Martin Liška <mliska@suse.cz> wrote:
> Hello.
>
> After receiving feedback from Richi and Wilco Dijkstra, I decided to fully not
> support not null-terminated strings. It brings more complications and the code has started
> to be overengineered. Thus c_getstr accepts only such strings and as a bonus it returns
> length of a string.
>
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>
> Ready to be installed?

+  /* Support only properly null-terminated strings.  */
+  if (string_length == 0
+      || string[string_length - 1] != '\0'
+      || offset > string_length)

shouldn't this be offset >= string_length?

Ok with that change.

Thanks,
Richard.

> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Check \0-termination of string in c_getstr (simplified version)
  2016-10-14  9:38               ` Richard Biener
@ 2016-10-14 11:10                 ` Martin Liška
  0 siblings, 0 replies; 45+ messages in thread
From: Martin Liška @ 2016-10-14 11:10 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On 10/14/2016 11:38 AM, Richard Biener wrote:
> On Thu, Oct 13, 2016 at 5:23 PM, Martin Liška <mliska@suse.cz> wrote:
>> Hello.
>>
>> After receiving feedback from Richi and Wilco Dijkstra, I decided to fully not
>> support not null-terminated strings. It brings more complications and the code has started
>> to be overengineered. Thus c_getstr accepts only such strings and as a bonus it returns
>> length of a string.
>>
>> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>>
>> Ready to be installed?
> 
> +  /* Support only properly null-terminated strings.  */
> +  if (string_length == 0
> +      || string[string_length - 1] != '\0'
> +      || offset > string_length)
> 
> shouldn't this be offset >= string_length?

Yes, it should be that, installed as r241152.

Thanks,
Martin

> 
> Ok with that change.
> 
> Thanks,
> Richard.
> 
>> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Fold __builtin_str{n}{case}cmp functions (simplified version 4)
  2016-10-13 15:25             ` [PATCH] Fold __builtin_str{n}{case}cmp functions (simplified version 4) Martin Liška
@ 2016-10-14 11:55               ` Richard Biener
  0 siblings, 0 replies; 45+ messages in thread
From: Richard Biener @ 2016-10-14 11:55 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Thu, Oct 13, 2016 at 5:24 PM, Martin Liška <mliska@suse.cz> wrote:
> Simplified version that just supports only null-terminated strings.
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>
> Ready to be installed?

Ok.

Richard.

> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Fold __builtin_memchr (simplified version 4)
  2016-10-13 15:26               ` [PATCH] Fold __builtin_memchr (simplified version 4) Martin Liška
@ 2016-10-14 11:57                 ` Richard Biener
  0 siblings, 0 replies; 45+ messages in thread
From: Richard Biener @ 2016-10-14 11:57 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Thu, Oct 13, 2016 at 5:26 PM, Martin Liška <mliska@suse.cz> wrote:
> Simplified version that supports only valid null-terminated string constants.
> Apart from that, I added checking for constant folding of expressions that
> have side effects.
>
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>
> Ready to be installed?

Ok.

Richard.

> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Test folding of str{n}{case}cmp and memchr (simplified version 4)
  2016-10-13 15:27             ` [PATCH] Test folding of str{n}{case}cmp and memchr (simplified version 4) Martin Liška
@ 2016-10-14 11:58               ` Richard Biener
  0 siblings, 0 replies; 45+ messages in thread
From: Richard Biener @ 2016-10-14 11:58 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Thu, Oct 13, 2016 at 5:26 PM, Martin Liška <mliska@suse.cz> wrote:
> Simplified version of tests, where I added tests for side effects.
>
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>
> Ready to be installed?

Ok.

Richard.

> Martin

^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2016-10-14 11:58 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-17  6:55 [PATCH 0/3] Better folding of 2 string builtin-ins marxin
2016-08-17  6:55 ` [PATCH 2/3] Smarter folding of __builtin_memchr marxin
2016-08-17  8:41   ` Richard Biener
2016-10-07  8:41     ` [PATCH 2/3] Fold __builtin_memchr (version 2) Martin Liška
2016-10-07 11:01       ` Richard Biener
2016-10-11  9:38         ` [PATCH] Fold __builtin_memchr (version 3) Martin Liška
2016-10-12  8:34           ` Richard Biener
2016-10-12  8:36           ` Richard Biener
2016-10-12 13:19             ` Martin Liška
2016-10-13 15:26               ` [PATCH] Fold __builtin_memchr (simplified version 4) Martin Liška
2016-10-14 11:57                 ` Richard Biener
2016-08-17  6:55 ` [PATCH 3/3] Test folding of strn{case}cmp and memchr marxin
2016-08-17  7:52   ` Martin Liška
2016-10-07  8:42     ` [PATCH 3/3] Test folding of str{n}{case}cmp and memchr (version 2) Martin Liška
2016-10-11  9:39       ` [PATCH] Test folding of str{n}{case}cmp and memchr (version 3) Martin Liška
2016-10-12  8:35         ` Richard Biener
2016-10-12 13:20           ` Martin Liška
2016-10-13 15:27             ` [PATCH] Test folding of str{n}{case}cmp and memchr (simplified version 4) Martin Liška
2016-10-14 11:58               ` Richard Biener
2016-08-17  6:55 ` [PATCH 1/3] Fold BUILT_IN_STRNCASECMP marxin
2016-08-17  7:10   ` Jakub Jelinek
2016-08-17  7:52     ` Martin Liška
2016-10-07  8:39   ` [PATCH 1/3] Fold __builtin_str{n}{case}cmp functions (version 2) Martin Liška
2016-10-07 10:50     ` Richard Biener
2016-10-11  9:26       ` Martin Liška
2016-10-11 10:27         ` Richard Biener
2016-10-11  9:28       ` [PATCH] Add a helper function: create_tmp Martin Liška
2016-10-11 10:30         ` Richard Biener
2016-10-11 10:31           ` Richard Biener
2016-10-12 10:50             ` Martin Liška
2016-10-11  9:28       ` [PATCH] Check \0-termination of string in c_getstr Martin Liška
2016-10-11 10:28         ` Richard Biener
2016-10-12 13:14           ` Martin Liška
2016-10-13 15:24             ` [PATCH] Check \0-termination of string in c_getstr (simplified version) Martin Liška
2016-10-14  9:38               ` Richard Biener
2016-10-14 11:10                 ` Martin Liška
2016-10-11  9:34       ` [PATCH] Fold __builtin_str{n}{case}cmp functions (version 3) Martin Liška
2016-10-12  8:30         ` Richard Biener
2016-10-12 13:17           ` Martin Liška
2016-10-13 15:25             ` [PATCH] Fold __builtin_str{n}{case}cmp functions (simplified version 4) Martin Liška
2016-10-14 11:55               ` Richard Biener
2016-10-12 13:48 ` [RFC] Possible folding opportunities for string built-ins Martin Liška
2016-10-12 15:55   ` Joseph Myers
2016-10-12 19:45     ` Jim Wilson
2016-10-13  8:38   ` Richard Biener

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).