public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] fold more string comparison with known result (PR 90879)
@ 2019-08-09 16:42 Martin Sebor
  2019-08-09 16:51 ` Jakub Jelinek
  2019-08-12 22:22 ` Jeff Law
  0 siblings, 2 replies; 21+ messages in thread
From: Martin Sebor @ 2019-08-09 16:42 UTC (permalink / raw)
  To: gcc-patches

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

GCC 9 optimizes a subset of expression of the form
(0 == strcmp(a, b)) based on the length and/or size of
the arguments but it doesn't take advantage of all
the opportunities there.  For example in the following,
although it folds the first test to false it doesn't fold
the second one:

   char a[4];

   void f (void)
   {
     if (strlen (a) > 3)   // folded to false by GCC 8+
       abort ();

     if (strcmp (a, "1234") == 0)   // folded by patched GCC
       abort ();
}

The attached patch extends the strcmp optimization added in
GCC 9 to also handle the latter cases (among others).  Testing
the enhancement with several other sizable code bases besides
GCC (Binutils/GDB, the Linux kernel, and LLVM) shows that code
like this is rare.  After thinking about it I decided it's more
likely a bug than a significant optimization opportunity, so
I introduced a new warning to point it out: -Wstring-compare
(enabled in -Wextra).

Besides this enhancement, the patch also improves the current
optimization to fold strcmp calls with conditional arguments
such as in:

   void f (char *s, int i)
   {
     strcpy (s, "12");
     if (strcmp (s, i ? "123" : "1234") == 0)   // folded
       abort ();
   }

Martin

PS The diff looks like the changes are more extensive than they
actually are.

[-- Attachment #2: gcc-90879.diff --]
[-- Type: text/x-patch, Size: 65782 bytes --]

PR tree-optimization/90879 - fold zero-equality of strcmp between a longer string and a smaller array

gcc/c-family/ChangeLog:

	PR tree-optimization/90879
	* c.opt (-Wstring-compare): New option.

gcc/testsuite/ChangeLog:

	PR tree-optimization/90879
	* gcc.dg/Wstring-compare-2.c: New test.
	* gcc.dg/Wstring-compare.c: New test.
	* gcc.dg/strcmpopt_3.c: Scan the optmized dump instead of strlen.
	* gcc.dg/strcmpopt_6.c: New test.
	* gcc.dg/strlenopt-65.c: Remove uinnecessary declarations, add
	test cases.
	* gcc.dg/strlenopt-66.c: Run it.
	* gcc.dg/strlenopt-67.c: New test.
	* gcc.dg/strlenopt-68.c: New test.

gcc/ChangeLog:

	PR tree-optimization/90879
	* builtins.c (check_access): Avoid using maxbound when null.
	* calls.c (maybe_warn_nonstring_arg): Adjust to get_range_strlen change.
	* doc/invoke.texi (-Wstring-compare): Document new warning option.
	* gengtype-state.c (state_ident_st): Use a zero-length array instead.
	(state_token_st): Same.  Make last.
	(state_ident_by_name): Allocate enough space for terminating nul.
	* gimple-fold.c (get_range_strlen_tree): Make setting maxbound
	conditional.
	(get_range_strlen): Overwrite initial maxbound when non-null.
	* gimple-ssa-sprintf.c (get_string_length): Adjust to get_range_strlen
	change.
	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Same.
	(used_only_for_zero_equality): New function.
	(handle_builtin_memcmp): Call it.
	(determine_min_objsize): Return an integer instead of tree.
	(get_len_or_size, strxcmp_eqz_result): New functions.
	(maybe_warn_pointless_strcmp): New function.
	(handle_builtin_string_cmp): Call it.  Fold zero-equality of strcmp
	between a longer string and a smaller array.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 695a9d191af..eca710942dc 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3326,7 +3326,7 @@ check_access (tree exp, tree, tree, tree dstwrite,
 	  c_strlen_data lendata = { };
 	  get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
 	  range[0] = lendata.minlen;
-	  range[1] = lendata.maxbound;
+	  range[1] = lendata.maxbound ? lendata.maxbound : lendata.maxlen;
 	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
 	      if (maxread && tree_int_cst_le (maxread, range[0]))
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 257cadfa5f1..2fe6cc4ee08 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -784,6 +784,12 @@ Wsizeof-array-argument
 C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
 Warn when sizeof is applied on a parameter declared as an array.
 
+Wstring-compare
+C ObjC C++ LTO ObjC++ Warning Var(warn_string_compare) Warning LangEnabledBy(C ObjC C++ ObjC++, Wextra)
+Warn about calls to strcmp and strncmp used in equality expressions that
+are necessarily true or false due to the length of one and size of the other
+argument.
+
 Wstringop-overflow
 C ObjC C++ LTO ObjC++ Warning Alias(Wstringop-overflow=, 2, 0)
 Warn about buffer overflow in string manipulation functions like memcpy
diff --git a/gcc/calls.c b/gcc/calls.c
index 7507b698e27..dcebf67b5cc 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1593,6 +1593,10 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	    if (!get_attr_nonstring_decl (arg))
 	      {
 		c_strlen_data lendata = { };
+		/* Set MAXBOUND to an arbitrary non-null non-integer
+		   node as a request to have it set to the length of
+		   the longest string in a PHI.  */
+		lendata.maxbound = arg;
 		get_range_strlen (arg, &lendata, /* eltsize = */ 1);
 		maxlen = lendata.maxbound;
 	      }
@@ -1618,6 +1622,10 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	if (!get_attr_nonstring_decl (arg))
 	  {
 	    c_strlen_data lendata = { };
+	    /* Set MAXBOUND to an arbitrary non-null non-integer
+	       node as a request to have it set to the length of
+	       the longest string in a PHI.  */
+	    lendata.maxbound = arg;
 	    get_range_strlen (arg, &lendata, /* eltsize = */ 1);
 	    maxlen = lendata.maxbound;
 	  }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 01aab60f895..f9efdc2e140 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -347,6 +347,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wsizeof-pointer-memaccess  -Wsizeof-array-argument @gol
 -Wstack-protector  -Wstack-usage=@var{byte-size}  -Wstrict-aliasing @gol
 -Wstrict-aliasing=n  -Wstrict-overflow  -Wstrict-overflow=@var{n} @gol
+-Wstring-compare @gol
 -Wstringop-overflow=@var{n}  -Wstringop-truncation  -Wsubobject-linkage @gol
 -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}malloc@r{]} @gol
 -Wsuggest-final-types @gol  -Wsuggest-final-methods  -Wsuggest-override @gol
@@ -5815,6 +5816,30 @@ comparisons, so this warning level gives a very large number of
 false positives.
 @end table
 
+@item -Wstring-compare
+@opindex Wstring-compare
+@opindex Wno-string-compare
+Warn for calls to @code{strcmp} and @code{strncmp} whose result can
+be determined to be either zero or non-zero in tests for such equality
+owing to the length of one argument being greater than the size of
+the array the other argument is stored in (or the bound in the case
+of @code{strncmp}.  Such calls could be mistakes.  For example, the call
+to @code{strcmp} below is diagnosed because its result is necessarily
+non-zero irrespective of the contents of the array @code{a}.
+
+@smallexample
+extern char a[4];
+void f (char *d)
+@{
+  strcpy (d, "string");
+  @dots{}
+  if (0 == strcmp (a, d))   // cannot be true
+    puts ("a and d are the same");
+@}
+@end smallexample
+
+@option{-Wstring-compare} is enabled by @option{-Wextra}.
+
 @item -Wstringop-overflow
 @itemx -Wstringop-overflow=@var{type}
 @opindex Wstringop-overflow
diff --git a/gcc/gengtype-state.c b/gcc/gengtype-state.c
index 03f40694ec6..80a8b57e9a2 100644
--- a/gcc/gengtype-state.c
+++ b/gcc/gengtype-state.c
@@ -79,6 +79,14 @@ enum state_token_en
   STOK_NAME                     /* hash-consed name or identifier.  */
 };
 
+/* Suppress warning: ISO C forbids zero-size array for stok_string
+   below.  The arrays are treated as flexible array members but in
+   otherwise an empty struct or as a member of a union cannot be
+   declared as such.  They must have zero size to keep GCC from
+   assuming their bound reflect their size.  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+
 
 /* Structure and hash-table used to share identifiers or names.  */
 struct state_ident_st
@@ -86,11 +94,10 @@ struct state_ident_st
   /* TODO: We could improve the parser by reserving identifiers for
      state keywords and adding a keyword number for them.  That would
      mean adding another field in this state_ident_st struct.  */
-  char stid_name[1];		/* actually bigger & null terminated */
+  char stid_name[0];		/* actually bigger & null terminated */
 };
 static htab_t state_ident_tab;
 
-
 /* The state_token_st structure is for lexical tokens in the read
    state file.  The stok_kind field discriminates the union.  Tokens
    are allocated by peek_state_token which calls read_a_state_token
@@ -110,14 +117,15 @@ struct state_token_st
   union		                        /* discriminated by stok_kind! */
   {
     int stok_num;			/* when STOK_INTEGER */
-    char stok_string[1];		/* when STOK_STRING, actual size is
-					   bigger and null terminated */
     struct state_ident_st *stok_ident;	/* when STOK_IDENT */
     void *stok_ptr;		        /* null otherwise */
+    char stok_string[0];		/* when STOK_STRING, actual size is
+					   bigger and null terminated */
   }
   stok_un;
 };
 
+#pragma GCC diagnostic pop
 
 
 
@@ -325,7 +333,7 @@ state_ident_by_name (const char *name, enum insert_option optins)
   namlen = strlen (name);
   stid =
     (struct state_ident_st *) xmalloc (sizeof (struct state_ident_st) +
-				       namlen);
+				       namlen + 1);
   memset (stid, 0, sizeof (struct state_ident_st) + namlen);
   strcpy (stid->stid_name, name);
   *slot = stid;
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index fc57fb45e3a..582768090ae 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1346,6 +1346,10 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind,
 	}
     }
 
+  /* Set if VAL represents the maximum length based on array size (set
+     when exact length cannot be determined).  */
+  bool maxbound = false;
+
   if (!val && rkind == SRK_LENRANGE)
     {
       if (TREE_CODE (arg) == ADDR_EXPR)
@@ -1441,6 +1445,7 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind,
 	      pdata->minlen = ssize_int (0);
 	    }
 	}
+      maxbound = true;
     }
 
   if (!val)
@@ -1454,7 +1459,7 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind,
 	  && tree_int_cst_lt (val, pdata->minlen)))
     pdata->minlen = val;
 
-  if (pdata->maxbound)
+  if (pdata->maxbound && TREE_CODE (pdata->maxbound) == INTEGER_CST)
     {
       /* Adjust the tighter (more optimistic) string length bound
 	 if necessary and proceed to adjust the more conservative
@@ -1472,7 +1477,10 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind,
       else
 	pdata->maxbound = val;
     }
-  else
+  else if (pdata->maxbound || maxbound)
+    /* Set PDATA->MAXBOUND only if it either isn't INTEGER_CST or
+       if VAL corresponds to the maximum length determined based
+       on the type of the object.  */
     pdata->maxbound = val;
 
   if (tight_bound)
@@ -1653,8 +1661,11 @@ get_range_strlen (tree arg, bitmap *visited,
 
 /* Try to obtain the range of the lengths of the string(s) referenced
    by ARG, or the size of the largest array ARG refers to if the range
-   of lengths cannot be determined, and store all in *PDATA.  ELTSIZE
-   is the expected size of the string element in bytes: 1 for char and
+   of lengths cannot be determined, and store all in *PDATA which must
+   be zero-initialized on input except PDATA->MAXBOUND may be set to
+   a non-null tree node other than INTEGER_CST to request to have it
+   set to the length of the longest string in a PHI.  ELTSIZE is
+   the expected size of the string element in bytes: 1 for char and
    some power of 2 for wide characters.
    Return true if the range [PDATA->MINLEN, PDATA->MAXLEN] is suitable
    for optimization.  Returning false means that a nonzero PDATA->MINLEN
@@ -1666,6 +1677,7 @@ bool
 get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize)
 {
   bitmap visited = NULL;
+  tree maxbound = pdata->maxbound;
 
   if (!get_range_strlen (arg, &visited, SRK_LENRANGE, pdata, eltsize))
     {
@@ -1678,8 +1690,9 @@ get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize)
   else if (!pdata->minlen)
     pdata->minlen = ssize_int (0);
 
-  /* Unless its null, leave the more conservative MAXBOUND unchanged.  */
-  if (!pdata->maxbound)
+  /* If it's unchanged from it initial non-null value, set the conservative
+     MAXBOUND to MAXLEN.  Otherwise leave it null (if it is null).  */
+  if (maxbound && pdata->maxbound == maxbound)
     pdata->maxbound = pdata->maxlen;
 
   if (visited)
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 88ba1f2cac1..279338b1577 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -2041,6 +2041,9 @@ get_string_length (tree str, unsigned eltsize)
      aren't known to point any such arrays result in LENDATA.MAXLEN
      set to SIZE_MAX.  */
   c_strlen_data lendata = { };
+  /* Set MAXBOUND to an arbitrary non-null non-integer node as a request
+     to have it set to the length of the longest string in a PHI.  */
+  lendata.maxbound = str;
   get_range_strlen (str, &lendata, eltsize);
 
   /* Return the default result when nothing is known about the string. */
diff --git a/gcc/testsuite/gcc.dg/Wstring-compare-2.c b/gcc/testsuite/gcc.dg/Wstring-compare-2.c
new file mode 100644
index 00000000000..7116be6896e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstring-compare-2.c
@@ -0,0 +1,127 @@
+/* PR tree-optimization/90879 - fold zero-equality of strcmp between
+   a longer string and a smaller array
+   Test for a warning for strcmp of a longer string against smaller
+   array.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wstring-compare -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void*, const void*, size_t);
+
+extern int strcmp (const char*, const char*);
+extern size_t strlen (const char*);
+extern char* strcpy (char*, const char*);
+extern char* strncpy (char*, const char*, size_t);
+extern int strncmp (const char*, const char*, size_t);
+
+void sink (int, ...);
+#define sink(...) sink (__LINE__, __VA_ARGS__)
+
+
+extern char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9];
+
+#define T(a, b) sink (0 == strcmp (a, b))
+
+
+void test_string_cst (void)
+{
+  const char *s1 = "1", *s2 = "12";
+
+  T (s1, a1);                 // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to non-zero" }
+  T (s1, a2);
+  T (s1, a3);
+
+  T (a1, s1);                 // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to non-zero" }
+  T (a2, s1);
+  T (a3, s1);
+
+  T (s2, a1);                 // { dg-warning ".strcmp. of a string of length 2 and an array of size 1 evaluates to non-zero" }
+  T (s2, a2);                 // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to non-zero" }
+  T (s2, a3);
+
+  T (a1, s2);                 // { dg-warning ".strcmp. of a string of length 2 and an array of size 1 evaluates to non-zero" }
+  T (a2, s2);                 // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to non-zero" }
+  T (a3, s2);
+}
+
+
+void test_string_cst_off_cst (void)
+{
+  const char *s1 = "1", *s2 = "12", *s3 = "123", *s4 = "1234";
+
+  T (s1, a2 + 1);              // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to non-zero" }
+  T (a2 + 1, s1);              // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to non-zero" }
+
+
+  T (s3 + 1, a2);             // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to non-zero" }
+  T (s3 + 1, a3);
+
+  T (s2, a4 + 1);
+  T (s2, a4 + 2);             // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to non-zero" }
+
+  T (s4, a4 + 1);             // { dg-warning ".strcmp. of a string of length 4 and an array of size 3 evaluates to non-zero" }
+  T (s3, a5 + 1);
+}
+
+
+/* Use strncpy below rather than memcpy until PR 91183 is resolved.  */
+
+#undef T
+#define T(s, n, a)					\
+  do {							\
+    char arr[32];					\
+    sink (arr);						\
+    strncpy (arr, s, n < 0 ? strlen (s) + 1: n);	\
+    sink (0 == strcmp (arr, a));			\
+  } while (0)
+
+void test_string_exact_length (void)
+{
+  const char *s1 = "1", *s2 = "12";
+
+  T (s1, -1, a1);             // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to non-zero" }
+  T (s1, -1, a2);
+  T (s1, -1, a3);
+
+  T (s2, -1, a1);             // { dg-warning ".strcmp. of a string of length 2 and an array of size 1 evaluates to non-zero" }
+  T (s2, -1, a2);             // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to non-zero" }
+  T (s2, -1, a3);
+}
+
+
+void test_string_min_length (void)
+{
+  const char *s1 = "1", *s2 = "12";
+
+  T (s1,  1, a1);             // { dg-warning ".strcmp. of a string of length 1 or more and an array of size 1 evaluates to non-zero" }
+  T (s1,  1, a2);
+  T (s1,  1, a3);
+
+  T (s2,  2, a1);             // { dg-warning ".strcmp. of a string of length 2 or more and an array of size 1 evaluates to non-zero" }
+  T (s2,  2, a2);             // { dg-warning ".strcmp. of a string of length 2 or more and an array of size 2 evaluates to non-zero" }
+  T (s2,  2, a3);
+}
+
+
+int test_strncmp_str_lit_var (const char *s, long n)
+{
+  if (strncmp (s, "123456", n) == 0)    // { dg-bogus "\\\[-Wstring-compare" }
+    return 1;
+
+  return 0;
+}
+
+int test_strlen_strncmp_str_lit_var (const char *s, long n)
+{
+  if (__builtin_strlen (s) < n)
+    return -1;
+
+  if (n == 6)
+    if (strncmp (s, "123456", n) == 0)  // { dg-bogus "\\\[-Wstring-compare" }
+      return 1;
+
+  return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/Wstring-compare.c b/gcc/testsuite/gcc.dg/Wstring-compare.c
new file mode 100644
index 00000000000..702abeb96be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstring-compare.c
@@ -0,0 +1,181 @@
+/* PR tree-optimization/90879 - fold zero-equality of strcmp between
+   a longer string and a smaller array
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#include "strlenopt.h"
+
+#define T(a, b) sink (0 == strcmp (a, b), a, b)
+
+void sink (int, ...);
+
+struct S { char a4[4], c; };
+
+extern char a4[4];
+extern char a5[5];
+extern char b4[4];
+
+/* Verify that comparison of string literals with arrays with unknown
+   content but size that prevents them from comparing equal is diagnosed.  */
+
+void strcmp_array_lit (void)
+{
+  if (strcmp (a4, "1234"))  // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero" }
+                            // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+    sink (0, a4);
+
+  int cmp;
+  cmp = strcmp (a4, "1234");  // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero" }
+  if (cmp)                  // { dg-message "in this expression" }
+    sink (0, a4);
+
+  T (a4, "4321");           // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero " }
+  T (a4, "12345");          // { dg-warning "length 5 and an array of size 4 " }
+  T (a4, "123456");         // { dg-warning "length 6 and an array of size 4 " }
+  T ("1234", a4);           // { dg-warning "length 4 and an array of size 4 " }
+  T ("12345", a4);          // { dg-warning "length 5 and an array of size 4 " }
+  T ("123456", a4);         // { dg-warning "length 6 and an array of size 4 " }
+}
+
+
+void strcmp_array_pstr (void)
+{
+  const char *s4 = "1234";
+
+  {
+    if (strcmp (a4, s4))    // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero" }
+                            // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+      sink (1, a4);
+    else
+      sink (0, a4);
+  }
+
+  {
+    int c;
+    c = strcmp (a4, s4);    // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero" }
+    if (c)                  // { dg-message "in this expression" }
+      sink (1, a4);
+    else
+      sink (0, a4);
+  }
+
+  const char *t4 = "4321";
+  const char *s5 = "12345";
+  const char *s6 = "123456";
+
+  T (a4, t4);               // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero " }
+  T (a4, s5);               // { dg-warning "length 5 and an array of size 4 " }
+  T (a4, s6);               // { dg-warning "length 6 and an array of size 4 " }
+  T (s4, a4);               // { dg-warning "length 4 and an array of size 4 " }
+  T (s5, a4);               // { dg-warning "length 5 and an array of size 4 " }
+  T (s6, a4);               // { dg-warning "length 6 and an array of size 4 " }
+}
+
+
+void strcmp_array_cond_pstr (int i)
+{
+  const char *s4 = i ? "1234" : "4321";
+  T (a4, s4);               // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero " }
+  T (a5, s4);
+}
+
+void strcmp_array_copy (void)
+{
+  char s[8];
+
+  {
+    strcpy (s, "1234");
+    if (strcmp (a4, s))     // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero" }
+                            // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+      sink (1, a4);
+    else
+      sink (0, a4);
+  }
+
+  {
+    strcpy (s, "1234");
+
+    int c;
+    c = strcmp (a4, s);     // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero" }
+    if (c)                  // { dg-message "in this expression" }
+      sink (1, a4);
+    else
+      sink (0, a4);
+  }
+
+  strcpy (s, "4321");
+  T (a4, s);                // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to non-zero " }
+  strcpy (s, "12345");
+  T (a4, s);                // { dg-warning "length 5 and an array of size 4 " }
+  strcpy (s, "123456");
+  T (a4, s);                // { dg-warning "length 6 and an array of size 4 " }
+  strcpy (s, "4321");
+  T (s, a4);                // { dg-warning "length 4 and an array of size 4 " }
+  strcpy (s, "54321");
+  T (s, a4);                // { dg-warning "length 5 and an array of size 4 " }
+  strcpy (s, "654321");
+  T (s, a4);                // { dg-warning "length 6 and an array of size 4 " }
+}
+
+
+void strcmp_member_array_lit (const struct S *p)
+{
+  T (p->a4, "1234");        // { dg-warning "length 4 and an array of size 4 " }
+}
+
+
+#undef T
+#define T(a, b, n) sink (0 == strncmp (a, b, n), a, b)
+
+void strncmp_array_lit (void)
+{
+  if (strncmp (a4, "12345", 5))   // { dg-warning "'strncmp' of a string of length 5, an array of size 4 and bound of 5 evaluates to non-zero" }
+                                  // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+    sink (0, a4);
+
+  int cmp;
+  cmp = strncmp (a4, "54321", 5);   // { dg-warning "'strncmp' of a string of length 5, an array of size 4 and bound of 5 evaluates to non-zero" }
+  if (cmp)                          // { dg-message "in this expression" }
+    sink (0, a4);
+
+  // Verify no warning when the bound is the same as the array size.
+  T (a4, "4321", 4);
+  T (a4, "654321", 4);
+
+  T (a4, "12345", 5);       // { dg-warning "length 5, an array of size 4 and bound of 5 " }
+  T (a4, "123456", 6);      // { dg-warning "length 6, an array of size 4 and bound of 6" }
+
+  T ("1234", a4, 4);
+  T ("12345", a4, 4);
+
+  T ("12345", a4, 5);       // { dg-warning "length 5, an array of size 4 and bound of 5 " }
+  T ("123456", a4, 6);      // { dg-warning "length 6, an array of size 4 and bound of 6 " }
+}
+
+
+void strncmp_strarray_copy (void)
+{
+  {
+    char a[] = "1234";
+    char b[6];
+    strcpy (b, "12345");
+    if (strncmp (a, b, 5))  // { dg-warning "'strncmp' of strings of length 4 and 5 and bound of 5 evaluates to non-zero" }
+                            // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+      sink (0, a, b);
+  }
+
+  {
+    char a[] = "4321";
+    char b[6];
+    strcpy (b, "54321");
+    int cmp;
+    cmp = strncmp (a, b, 5);  // { dg-warning "'strncmp' of strings of length 4 and 5 and bound of 5 evaluates to non-zero" }
+    if (cmp)                  // { dg-message "in this expression" }
+      sink (0, a, b);
+  }
+
+  strcpy (a4, "abc");
+  T (a4, "54321", 5);       // { dg-warning "'strncmp' of strings of length 3 and 5 and bound of 5 evaluates to non-zero " }
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_3.c b/gcc/testsuite/gcc.dg/strcmpopt_3.c
index 86a0d7a08b3..35941bee575 100644
--- a/gcc/testsuite/gcc.dg/strcmpopt_3.c
+++ b/gcc/testsuite/gcc.dg/strcmpopt_3.c
@@ -1,31 +1,31 @@
 /* { dg-do run } */
-/* { dg-options "-O2 -fdump-tree-strlen" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
 
-__attribute__ ((noinline)) int 
-f1 (void) 
-{ 
+__attribute__ ((noinline)) int
+f1 (void)
+{
   char *s0= "abcd";
   char s[8];
   __builtin_strcpy (s, s0);
-  return __builtin_strcmp(s, "abc") != 0; 
+  return __builtin_strcmp (s, "abc") != 0;
 }
 
 __attribute__ ((noinline)) int
-f2 (void) 
-{ 
+f2 (void)
+{
   char *s0 = "ab";
   char s[8];
   __builtin_strcpy (s, s0);
-  return __builtin_strcmp("abc", s) != 0; 
+  return __builtin_strcmp ("abc", s) != 0;
 }
 
 int main (void)
 {
-  if (f1 () != 1 
+  if (f1 () != 1
       || f2 () != 1)
     __builtin_abort ();
 
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strcmp" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_6.c b/gcc/testsuite/gcc.dg/strcmpopt_6.c
new file mode 100644
index 00000000000..cb99294e5fa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strcmpopt_6.c
@@ -0,0 +1,207 @@
+/* Verify that strcmp and strncmp calls with mixed constant and
+   non-constant strings are evaluated correctly.
+   { dg-do run }
+   { dg-options "-O2" } */
+
+#include "strlenopt.h"
+
+#define A(expr)                                                 \
+  ((expr)                                                       \
+   ? (void)0                                                    \
+   : (__builtin_printf ("assertion failed on line %i: %s\n",    \
+                        __LINE__, #expr),                       \
+      __builtin_abort ()))
+
+__attribute__ ((noclone, noinline)) int
+test_strlen_gt2_strcmp_abcd (const char *s)
+{
+  if (strlen (s) < 3)
+    return -1;
+
+  return strcmp (s, "abcd") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strlen_lt6_strcmp_abcd (const char *s)
+{
+  if (strlen (s) > 5)
+    return -1;
+
+  return strcmp (s, "abcd") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_strcmp_abc (const char *s)
+{
+  char a[4];
+  strcpy (a, s);
+  return strcmp (a, "abc") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_abc_strcmp (const char *s)
+{
+  char a[4], b[6];
+  strcpy (a, "abc");
+  strcpy (b, s);
+  return strcmp (a, b) == 0;
+}
+
+/* Exercise strcmp of two strings between 1 and 3 characters long
+   stored in arrays of the same known size.  */
+char ga4[4], gb4[4];
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strcmp_same_size_arrays (void)
+{
+  ga4[0] = gb4[0] = 'x';
+  ga4[3] = gb4[3] = '\0';
+  return strcmp (ga4, gb4) == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strncmp_bound_2_same_size_arrays (void)
+{
+  ga4[0] = gb4[0] = 'x';
+  ga4[3] = gb4[3] = '\0';
+  return strncmp (ga4, gb4, 2) == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strncmp_bound_equal_same_size_arrays (void)
+{
+  ga4[0] = gb4[0] = 'x';
+  ga4[3] = gb4[3] = '\0';
+  return strncmp (ga4, gb4, 4) == 0;
+}
+
+/* Exercise strcmp of two strings between 0 and 3 characters long
+   stored in arrays of the same known size.  */
+
+__attribute__ ((noclone, noinline)) int
+test_nulterm_strcmp_same_size_arrays (void)
+{
+  ga4[3] = gb4[3] = '\0';
+  return strcmp (ga4, gb4) == 0;
+}
+
+/* Exercise strcmp of two strings between 1 and 3 and 1 and 4 characters
+   long, respectively, stored in arrays of known but different sizes.  */
+char gc5[5];
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strcmp_arrays (void)
+{
+  ga4[0] = gc5[0] = 'x';
+  ga4[3] = gc5[4] = '\0';
+  return strcmp (ga4, gc5) == 0;
+}
+
+/* Exercise strcmp of two strings between 0 and 3 and 1 and 4 characters
+   long, respectively, stored in arrays of known but different sizes.  */
+
+__attribute__ ((noclone, noinline)) int
+test_nulterm_strcmp_arrays (void)
+{
+  ga4[3] = gc5[4] = '\0';
+  return strcmp (ga4, gc5) == 0;
+}
+
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_strncmp_abcd (const char *s)
+{
+  char a[6];
+  strcpy (a, s);
+  return strcmp (a, "abcd") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_abcd_strncmp_3 (const char *s)
+{
+  char a[6], b[8];
+  strcpy (a, "abcd");
+  strcpy (b, s);
+  return strncmp (a, b, 3) == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_abcd_strncmp_4 (const char *s)
+{
+  char a[6], b[8];
+  strcpy (a, "abcd");
+  strcpy (b, s);
+  return strncmp (a, b, 4) == 0;
+}
+
+
+int main (void)
+{
+  test_strlen_gt2_strcmp_abcd ("abcd");
+  test_strlen_lt6_strcmp_abcd ("abcd");
+
+  A (0 == test_strcpy_strcmp_abc ("ab"));
+  A (0 != test_strcpy_strcmp_abc ("abc"));
+  A (0 == test_strcpy_strcmp_abc ("abcd"));
+
+  A (0 == test_strcpy_abc_strcmp ("ab"));
+  A (0 != test_strcpy_abc_strcmp ("abc"));
+  A (0 == test_strcpy_abc_strcmp ("abcd"));
+
+  strcpy (ga4, "abc"); strcpy (gb4, "abd");
+  A (0 == test_store_0_nulterm_strcmp_same_size_arrays ());
+  strcpy (ga4, "abd"); strcpy (gb4, "abc");
+  A (0 == test_store_0_nulterm_strcmp_same_size_arrays ());
+  strcpy (ga4, "abc"); strcpy (gb4, "abc");
+  A (0 != test_store_0_nulterm_strcmp_same_size_arrays ());
+
+  strcpy (ga4, "abc"); strcpy (gb4, "acd");
+  A (0 == test_store_0_nulterm_strncmp_bound_2_same_size_arrays ());
+  strcpy (ga4, "acd"); strcpy (gb4, "abc");
+  A (0 == test_store_0_nulterm_strncmp_bound_2_same_size_arrays ());
+  strcpy (ga4, "abc"); strcpy (gb4, "abc");
+  A (0 != test_store_0_nulterm_strncmp_bound_2_same_size_arrays ());
+
+  strcpy (ga4, "abc"); strcpy (gb4, "abd");
+  A (0 == test_store_0_nulterm_strncmp_bound_equal_same_size_arrays ());
+  strcpy (ga4, "abd"); strcpy (gb4, "abc");
+  A (0 == test_store_0_nulterm_strncmp_bound_equal_same_size_arrays ());
+  strcpy (ga4, "abc"); strcpy (gb4, "abc");
+  A (0 != test_store_0_nulterm_strncmp_bound_equal_same_size_arrays ());
+
+  strcpy (ga4, "abc"); strcpy (gb4, "abd");
+  A (0 == test_nulterm_strcmp_same_size_arrays ());
+  strcpy (ga4, "abd"); strcpy (gb4, "abc");
+  A (0 == test_nulterm_strcmp_same_size_arrays ());
+  strcpy (ga4, "abc"); strcpy (gb4, "abc");
+  A (0 != test_nulterm_strcmp_same_size_arrays ());
+
+  strcpy (ga4, "abc"); strcpy (gc5, "abcd");
+  A (0 == test_store_0_nulterm_strcmp_arrays ());
+  strcpy (ga4, "abd"); strcpy (gc5, "abcd");
+  A (0 == test_store_0_nulterm_strcmp_arrays ());
+  strcpy (ga4, "abc"); strcpy (gc5, "abc");
+  A (0 != test_store_0_nulterm_strcmp_arrays ());
+
+  strcpy (ga4, "abc"); strcpy (gc5, "abcd");
+  A (0 == test_nulterm_strcmp_arrays ());
+  strcpy (ga4, "abd"); strcpy (gc5, "abc");
+  A (0 == test_nulterm_strcmp_arrays ());
+  strcpy (ga4, "abc"); strcpy (gc5, "abc");
+  A (0 != test_nulterm_strcmp_arrays ());
+
+  A (0 == test_strcpy_strncmp_abcd ("ab"));
+  A (0 == test_strcpy_strncmp_abcd ("abc"));
+  A (0 != test_strcpy_strncmp_abcd ("abcd"));
+  A (0 == test_strcpy_strncmp_abcd ("abcde"));
+
+  A (0 == test_strcpy_abcd_strncmp_3 ("ab"));
+  A (0 != test_strcpy_abcd_strncmp_3 ("abc"));
+  A (0 != test_strcpy_abcd_strncmp_3 ("abcd"));
+  A (0 != test_strcpy_abcd_strncmp_3 ("abcde"));
+
+  A (0 == test_strcpy_abcd_strncmp_4 ("ab"));
+  A (0 == test_strcpy_abcd_strncmp_4 ("abc"));
+  A (0 != test_strcpy_abcd_strncmp_4 ("abcd"));
+  A (0 != test_strcpy_abcd_strncmp_4 ("abcde"));
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-65.c b/gcc/testsuite/gcc.dg/strlenopt-65.c
index a34d178faa1..521d7ac2b42 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-65.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-65.c
@@ -1,17 +1,10 @@
 /* PRE tree-optimization/90626 - fold strcmp(a, b) == 0 to zero when
    one string length is exact and the other is unequal
    { dg-do compile }
-   { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+   { dg-options "-O2 -Wall -Wno-string-compare -fdump-tree-optimized -ftrack-macro-expansion=0" } */
 
 #include "strlenopt.h"
 
-typedef __SIZE_TYPE__ size_t;
-
-extern void abort (void);
-extern void* memcpy (void *, const void *, size_t);
-extern int strcmp (const char *, const char *);
-extern int strncmp (const char *, const char *, size_t);
-
 #define CAT(x, y) x ## y
 #define CONCAT(x, y) CAT (x, y)
 #define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
@@ -142,21 +135,45 @@ void test_strcmp_keep (const char *s, const char *t)
 #undef CMPFUNC
 #define CMPFUNC(a, b, dummy) strcmp (a, b)
 
-  KEEP ("1", "1", a, b, -1);
+  KEEP ("123", "123\0", a, b, /* bnd = */ -1);
+  KEEP ("123\0", "123", a, b, -1);
+
+  {
+    char a[8], b[8];
+    sink (a, b);
+    strcpy (a, s);
+    strcpy (b, t);
+    TEST_KEEP (0 == strcmp (a, b));
+  }
+}
+
+
+void test_strncmp_keep (const char *s, const char *t)
+{
+#undef CMPFUNC
+#define CMPFUNC(a, b, n) strncmp (a, b, n)
+
+  KEEP ("1", "1", a, b, 2);
 
-  KEEP ("1\0", "1", a, b, -1);
-  KEEP ("1",   "1\0", a, b, -1);
+  KEEP ("1\0", "1", a, b, 2);
+  KEEP ("1",   "1\0", a, b, 2);
 
-  KEEP ("12\0", "12", a, b, -1);
-  KEEP ("12",   "12\0", a, b, -1);
+  KEEP ("12\0", "12", a, b, 2);
+  KEEP ("12",   "12\0", a, b, 2);
 
-  KEEP ("111\0", "111", a, b, -1);
-  KEEP ("112", "112\0", a, b, -1);
+  KEEP ("111\0", "111", a, b, 3);
+  KEEP ("112", "112\0", a, b, 3);
 
-  KEEP (s, t, a, b, -1);
+  {
+    char a[8], b[8];
+    sink (a, b);
+    strcpy (a, s);
+    strcpy (b, t);
+    TEST_KEEP (0 == strncmp (a, b, sizeof a));
+  }
 }
 
 /* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
 
-   { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 8 "optimized" } }
-   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 8 "optimized" } } */
+   { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 11 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 11 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-66.c b/gcc/testsuite/gcc.dg/strlenopt-66.c
index 5dc10a07d3d..4ba31a845b0 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-66.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-66.c
@@ -1,6 +1,6 @@
 /* PRE tree-optimization/90626 - fold strcmp(a, b) == 0 to zero when
    one string length is exact and the other is unequal
-   { dg-do compile }
+   { dg-do run }
    { dg-options "-O2 -Wall -fdump-tree-optimized" } */
 
 #include "strlenopt.h"
@@ -65,8 +65,44 @@ test_strncmp (void)
   A (0 <  strncmp (b, a, 5));
 }
 
+
+__attribute__ ((noclone, noinline, noipa)) void
+test_strncmp_a4_cond_s5_s2_2 (const char *s, int i)
+{
+  char a4[4];
+  strcpy (a4, s);
+  A (0 == strncmp (a4, i ? "12345" : "12", 2));
+}
+
+
+__attribute__ ((noclone, noinline, noipa)) void
+test_strncmp_a4_cond_a5_s2_5 (const char *s, const char *t, int i)
+{
+  char a4[4], a5[5];
+  strcpy (a4, s);
+  strcpy (a5, t);
+  A (0 == strncmp (a4, i ? a5 : "12", 5));
+}
+
+__attribute__ ((noclone, noinline, noipa)) void
+test_strncmp_a4_cond_a5_a3_n (const char *s1, const char *s2, const char *s3,
+			      int i, unsigned n)
+{
+  char a3[3], a4[4], a5[5];
+  strcpy (a3, s1);
+  strcpy (a4, s2);
+  strcpy (a5, s3);
+  A (0 == strncmp (a4, i ? a5 : a3, n));
+}
+
+
 int main (void)
 {
   test_strcmp ();
   test_strncmp ();
+  test_strncmp_a4_cond_s5_s2_2 ("12", 0);
+  test_strncmp_a4_cond_a5_s2_5 ("12", "1234", 0);
+
+  test_strncmp_a4_cond_a5_a3_n ("12", "123", "1234", 0, 2);
+  test_strncmp_a4_cond_a5_a3_n ("123", "12", "12", 1, 3);
 }
diff --git a/gcc/testsuite/gcc.dg/strlenopt-68.c b/gcc/testsuite/gcc.dg/strlenopt-68.c
new file mode 100644
index 00000000000..46ceb9ddb05
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-68.c
@@ -0,0 +1,126 @@
+/* PR tree-optimization/90879 - fold zero-equality of strcmp between
+   a longer string and a smaller array
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-string-compare -fdump-tree-optimized -ftrack-macro-expansion=0" } */
+
+#include "strlenopt.h"
+
+#define A(expr)                                                 \
+  ((expr)                                                       \
+   ? (void)0                                                    \
+   : (__builtin_printf ("assertion failed on line %i: %s\n",    \
+                        __LINE__, #expr),                       \
+      __builtin_abort ()))
+
+void clobber (void*, ...);
+
+struct S { char a4[4], c; };
+
+extern char a4[4];
+extern char b4[4];
+
+/* Verify that comparison of string literals with arrays with unknown
+   content but size that prevents them from comparing equal is folded
+   to a constant.  */
+
+void test_array_lit (void)
+{
+  A (strcmp (a4, "1234")); clobber (a4);
+  A (strcmp (a4, "12345")); clobber (a4);
+  A (strcmp (a4, "123456")); clobber (a4);
+  A (strcmp ("1234", a4)); clobber (a4);
+  A (strcmp ("12345", a4)); clobber (a4);
+  A (strcmp ("123456", a4)); clobber (a4);
+}
+
+void test_memarray_lit (struct S *p)
+{
+  A (strcmp (p->a4, "1234"));
+  A (strcmp (p->a4, "12345"));
+  A (strcmp (p->a4, "123456"));
+
+  A (strcmp ("1234", p->a4));
+  A (strcmp ("12345", p->a4));
+  A (strcmp ("123456", p->a4));
+}
+
+/* Verify that the equality of empty strings is folded.  */
+
+void test_empty_string (void)
+{
+  A (0 == strcmp ("", ""));
+
+  *a4 = '\0';
+  A (0 == strcmp (a4, ""));
+  A (0 == strcmp ("", a4));
+  A (0 == strcmp (a4, a4));
+
+  char s[8] = "";
+  A (0 == strcmp (a4, s));
+
+  a4[1] = '\0';
+  b4[1] = '\0';
+  A (0 == strcmp (a4 + 1, b4 + 1));
+
+  a4[2] = '\0';
+  b4[2] = '\0';
+  A (0 == strcmp (&a4[2], &b4[2]));
+
+  clobber (a4, b4);
+
+  memset (a4, 0, sizeof a4);
+  memset (b4, 0, sizeof b4);
+  A (0 == strcmp (a4, b4));
+}
+
+/* Verify that comparison of dynamically created strings with unknown
+   arrays is folded.  */
+
+void test_array_copy (void)
+{
+  char s[8];
+  strcpy (s, "1234");
+  A (strcmp (a4, s));
+
+  strcpy (s, "12345");
+  A (strlen (s) == 5);
+  A (strcmp (a4, s)); clobber (a4);
+
+  strcpy (s, "123456");
+  A (strcmp (a4, s)); clobber (a4);
+
+  strcpy (s, "1234");
+  A (strcmp (s, a4)); clobber (a4);
+
+  strcpy (s, "12345");
+  A (strcmp (s, a4)); clobber (a4);
+
+  strcpy (s, "123456");
+  A (strcmp (s, a4)); clobber (a4);
+}
+
+
+void test_array_bounded (void)
+{
+  A (strncmp (a4, "12345", 5)); clobber (a4);
+  A (strncmp ("54321", a4, 5)); clobber (a4);
+
+  A (strncmp (a4, "123456", 5)); clobber (a4);
+  A (strncmp ("654321", a4, 5)); clobber (a4);
+}
+
+void test_array_copy_bounded (void)
+{
+  char s[8];
+  strcpy (s, "12345");
+  A (strncmp (a4, s, 5)); clobber (a4);
+  strcpy (s, "54321");
+  A (strncmp (s, a4, 5)); clobber (a4);
+
+  strcpy (s, "123456");
+  A (strncmp (a4, s, 5)); clobber (a4);
+  strcpy (s, "654321");
+  A (strncmp (s, a4, 5)); clobber (a4);
+}
+
+/* { dg-final { scan-tree-dump-not "abort|strcmp|strncmp" "optimized" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 4af47855e7c..29d1ae80abf 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -2091,6 +2091,9 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
   else
     {
       c_strlen_data lendata = { };
+      /* Set MAXBOUND to an arbitrary non-null non-integer node as a request
+	 to have it set to the length of the longest string in a PHI.  */
+      lendata.maxbound = src;
       get_range_strlen (src, &lendata, /* eltsize = */1);
       if (TREE_CODE (lendata.minlen) == INTEGER_CST
 	  && TREE_CODE (lendata.maxbound) == INTEGER_CST)
@@ -2862,51 +2865,78 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
   return true;
 }
 
-/* Handle a call to memcmp.  We try to handle small comparisons by
-   converting them to load and compare, and replacing the call to memcmp
-   with a __builtin_memcmp_eq call where possible.
-   return true when call is transformed, return false otherwise.  */
+/* Return a pointer to the first such equality expression if RES is used
+   only in experessions testing its equality to zero, and null otherwise.  */
 
-static bool
-handle_builtin_memcmp (gimple_stmt_iterator *gsi)
+static gimple*
+used_only_for_zero_equality (tree res)
 {
-  gcall *stmt2 = as_a <gcall *> (gsi_stmt (*gsi));
-  tree res = gimple_call_lhs (stmt2);
-  tree arg1 = gimple_call_arg (stmt2, 0);
-  tree arg2 = gimple_call_arg (stmt2, 1);
-  tree len = gimple_call_arg (stmt2, 2);
-  unsigned HOST_WIDE_INT leni;
+  gimple *first_use = NULL;
+
   use_operand_p use_p;
   imm_use_iterator iter;
 
-  if (!res)
-    return false;
-
   FOR_EACH_IMM_USE_FAST (use_p, iter, res)
     {
-      gimple *ustmt = USE_STMT (use_p);
+      gimple *use_stmt = USE_STMT (use_p);
 
-      if (is_gimple_debug (ustmt))
-	continue;
-      if (gimple_code (ustmt) == GIMPLE_ASSIGN)
+      if (is_gimple_debug (use_stmt))
+        continue;
+      if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
 	{
-	  gassign *asgn = as_a <gassign *> (ustmt);
-	  tree_code code = gimple_assign_rhs_code (asgn);
-	  if ((code != EQ_EXPR && code != NE_EXPR)
-	      || !integer_zerop (gimple_assign_rhs2 (asgn)))
-	    return false;
+	  tree_code code = gimple_assign_rhs_code (use_stmt);
+	  if (code == COND_EXPR)
+	    {
+	      tree cond_expr = gimple_assign_rhs1 (use_stmt);
+	      if ((TREE_CODE (cond_expr) != EQ_EXPR
+		   && (TREE_CODE (cond_expr) != NE_EXPR))
+		  || !integer_zerop (TREE_OPERAND (cond_expr, 1)))
+		return NULL;
+	    }
+	  else if (code == EQ_EXPR || code == NE_EXPR)
+	    {
+	      if (!integer_zerop (gimple_assign_rhs2 (use_stmt)))
+		return NULL;
+            }
+	  else
+	    return NULL;
 	}
-      else if (gimple_code (ustmt) == GIMPLE_COND)
+      else if (gimple_code (use_stmt) == GIMPLE_COND)
 	{
-	  tree_code code = gimple_cond_code (ustmt);
+	  tree_code code = gimple_cond_code (use_stmt);
 	  if ((code != EQ_EXPR && code != NE_EXPR)
-	      || !integer_zerop (gimple_cond_rhs (ustmt)))
-	    return false;
+	      || !integer_zerop (gimple_cond_rhs (use_stmt)))
+	    return NULL;
 	}
       else
-	return false;
+        return NULL;
+
+      if (!first_use)
+	first_use = use_stmt;
     }
 
+  return first_use;
+}
+
+/* Handle a call to memcmp.  We try to handle small comparisons by
+   converting them to load and compare, and replacing the call to memcmp
+   with a __builtin_memcmp_eq call where possible.
+   return true when call is transformed, return false otherwise.  */
+
+static bool
+handle_builtin_memcmp (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree res = gimple_call_lhs (stmt);
+
+  if (!res || !used_only_for_zero_equality (res))
+    return false;
+
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  unsigned HOST_WIDE_INT leni;
+
   if (tree_fits_uhwi_p (len)
       && (leni = tree_to_uhwi (len)) <= GET_MODE_SIZE (word_mode)
       && pow2p_hwi (leni))
@@ -2919,7 +2949,7 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
       if (int_mode_for_size (leni, 1).exists (&mode)
 	  && (align >= leni || !targetm.slow_unaligned_access (mode, align)))
 	{
-	  location_t loc = gimple_location (stmt2);
+	  location_t loc = gimple_location (stmt);
 	  tree type, off;
 	  type = build_nonstandard_integer_type (leni, 1);
 	  gcc_assert (known_eq (GET_MODE_BITSIZE (TYPE_MODE (type)), leni));
@@ -2943,78 +2973,10 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
 	}
     }
 
-  gimple_call_set_fndecl (stmt2, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
+  gimple_call_set_fndecl (stmt, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
   return true;
 }
 
-/* If IDX1 and IDX2 refer to strings A and B of unequal lengths, return
-   the result of 0 == strncmp (A, B, N) (which is the same as strcmp for
-   sufficiently large N).  Otherwise return false.  */
-
-static bool
-strxcmp_unequal (int idx1, int idx2, unsigned HOST_WIDE_INT n)
-{
-  unsigned HOST_WIDE_INT len1;
-  unsigned HOST_WIDE_INT len2;
-
-  bool nulterm1;
-  bool nulterm2;
-
-  if (idx1 < 0)
-    {
-      len1 = ~idx1;
-      nulterm1 = true;
-    }
-  else if (strinfo *si = get_strinfo (idx1))
-    {
-      if (tree_fits_uhwi_p (si->nonzero_chars))
-	{
-	  len1 = tree_to_uhwi (si->nonzero_chars);
-	  nulterm1 = si->full_string_p;
-	}
-      else
-	return false;
-    }
-  else
-    return false;
-
-  if (idx2 < 0)
-    {
-      len2 = ~idx2;
-      nulterm2 = true;
-    }
-  else if (strinfo *si = get_strinfo (idx2))
-    {
-      if (tree_fits_uhwi_p (si->nonzero_chars))
-	{
-	  len2 = tree_to_uhwi (si->nonzero_chars);
-	  nulterm2 = si->full_string_p;
-	}
-      else
-	return false;
-    }
-  else
-    return false;
-
-  /* N is set to UHWI_MAX for strcmp and less to strncmp.  Adjust
-     the length of each string to consider to be no more than N.  */
-  if (len1 > n)
-    len1 = n;
-  if (len2 > n)
-    len2 = n;
-
-  if ((len1 < len2 && nulterm1)
-      || (len2 < len1 && nulterm2))
-    /* The string lengths are definitely unequal and the result can
-       be folded to one (since it's used for comparison with zero).  */
-    return true;
-
-  /* The string lengths may be equal or unequal.  Even when equal and
-     both strings nul-terminated, without the string contents there's
-     no way to determine whether they are equal.  */
-  return false;
-}
-
 /* Given an index to the strinfo vector, compute the string length
    for the corresponding string. Return -1 when unknown.  */
 
@@ -3043,15 +3005,16 @@ compute_string_length (int idx)
 
 /* Determine the minimum size of the object referenced by DEST expression
    which must have a pointer type.
-   Return the minimum size of the object if successful or NULL when the size
-   cannot be determined.  */
-static tree
+   Return the minimum size of the object if successful or HWI_M1U when
+   the size cannot be determined.  */
+
+static unsigned HOST_WIDE_INT
 determine_min_objsize (tree dest)
 {
   unsigned HOST_WIDE_INT size = 0;
 
   if (compute_builtin_object_size (dest, 2, &size))
-    return build_int_cst (sizetype, size);
+    return size;
 
   /* Try to determine the size of the object through the RHS
      of the assign statement.  */
@@ -3059,11 +3022,11 @@ determine_min_objsize (tree dest)
     {
       gimple *stmt = SSA_NAME_DEF_STMT (dest);
       if (!is_gimple_assign (stmt))
-	return NULL_TREE;
+	return HOST_WIDE_INT_M1U;
 
       if (!gimple_assign_single_p (stmt)
 	  && !gimple_assign_unary_nop_p (stmt))
-	return NULL_TREE;
+	return HOST_WIDE_INT_M1U;
 
       dest = gimple_assign_rhs1 (stmt);
       return determine_min_objsize (dest);
@@ -3071,7 +3034,7 @@ determine_min_objsize (tree dest)
 
   /* Try to determine the size of the object from its type.  */
   if (TREE_CODE (dest) != ADDR_EXPR)
-    return NULL_TREE;
+    return HOST_WIDE_INT_M1U;
 
   tree type = TREE_TYPE (dest);
   if (TREE_CODE (type) == POINTER_TYPE)
@@ -3079,196 +3042,413 @@ determine_min_objsize (tree dest)
 
   type = TYPE_MAIN_VARIANT (type);
 
-  /* We cannot determine the size of the array if it's a flexible array,
-     which is declared at the end of a structure.  */
-  if (TREE_CODE (type) == ARRAY_TYPE
-      && !array_at_struct_end_p (dest))
+  /* The size of a flexible array cannot be determined.  Otherwise,
+     for arrays with more than one element, return the size of its
+     type.  GCC itself misuses arrays of both zero and one elements
+     as flexible array members so they are excluded as well.  */
+  if (TREE_CODE (type) != ARRAY_TYPE
+      || !array_at_struct_end_p (dest))
     {
-      tree size_t = TYPE_SIZE_UNIT (type);
-      if (size_t && TREE_CODE (size_t) == INTEGER_CST
-	  && !integer_zerop (size_t))
-        return size_t;
+      tree type_size = TYPE_SIZE_UNIT (type);
+      if (type_size && TREE_CODE (type_size) == INTEGER_CST
+	  && !integer_onep (type_size)
+	  && !integer_zerop (type_size))
+        return tree_to_uhwi (type_size);
     }
 
-  return NULL_TREE;
+  return HOST_WIDE_INT_M1U;
 }
 
-/* Handle a call to strcmp or strncmp. When the result is ONLY used to do
-   equality test against zero:
-
-   A. When the lengths of both arguments are constant and it's a strcmp:
-      * if the lengths are NOT equal, we can safely fold the call
-        to a non-zero value.
-      * otherwise, do nothing now.
-
-   B. When the length of one argument is constant, try to replace the call
-   with a __builtin_str(n)cmp_eq call where possible, i.e:
-
-   strncmp (s, STR, C) (!)= 0 in which, s is a pointer to a string, STR
-   is a string with constant length , C is a constant.
-     if (C <= strlen(STR) && sizeof_array(s) > C)
-       {
-         replace this call with
-         strncmp_eq (s, STR, C) (!)= 0
-       }
-     if (C > strlen(STR)
-       {
-         it can be safely treated as a call to strcmp (s, STR) (!)= 0
-         can handled by the following strcmp.
-       }
-
-   strcmp (s, STR) (!)= 0 in which, s is a pointer to a string, STR
-   is a string with constant length.
-     if  (sizeof_array(s) > strlen(STR))
-       {
-         replace this call with
-         strcmp_eq (s, STR, strlen(STR)+1) (!)= 0
-       }
-
-   Return true when the call is transformed, return false otherwise.
- */
+/* Given strinfo IDX for ARG, set LENRNG[] to the range of lengths
+   of  the string(s) referenced by ARG if it can be determined.
+   If the length cannot be determined, set *SIZE to the size of
+   the array the string is stored in, if any.  If no such array is
+   known, set *SIZE to -1.  When the strings are nul-terminated set
+   *NULTERM to true, otherwise to false.  Return true on success.  */
 
 static bool
-handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
+get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2],
+		 unsigned HOST_WIDE_INT *size, bool *nulterm)
 {
-  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
-  tree res = gimple_call_lhs (stmt);
-  use_operand_p use_p;
-  imm_use_iterator iter;
-  tree arg1 = gimple_call_arg (stmt, 0);
-  tree arg2 = gimple_call_arg (stmt, 1);
-  int idx1 = get_stridx (arg1);
-  int idx2 = get_stridx (arg2);
-  HOST_WIDE_INT length = -1;
-  bool is_ncmp = false;
-
-  if (!res)
-    return false;
+  /* Set so that both LEN and ~LEN are invalid lengths, i.e.,
+     maximum possible length + 1.  */
+  lenrng[0] = lenrng[1] = HOST_WIDE_INT_MAX;
 
-  /* When both arguments are unknown, do nothing.  */
-  if (idx1 == 0 && idx2 == 0)
-    return false;
+  *size = HOST_WIDE_INT_M1U;
 
-  /* Handle strncmp function.  */
-  if (gimple_call_num_args (stmt) == 3)
+  if (idx < 0)
     {
-      tree len = gimple_call_arg (stmt, 2);
-      if (tree_fits_shwi_p (len))
-        length = tree_to_shwi (len);
-
-      is_ncmp = true;
+      /* IDX is the inverted constant string length.  */
+      lenrng[0] = ~idx;
+      lenrng[1] = lenrng[0];
+      *nulterm = true;
     }
-
-  /* For strncmp, if the length argument is NOT known, do nothing.  */
-  if (is_ncmp && length < 0)
-    return false;
-
-  /* When the result is ONLY used to do equality test against zero.  */
-  FOR_EACH_IMM_USE_FAST (use_p, iter, res)
+  else if (idx == 0)
+    ; /* Handled below.  */
+  else if (strinfo *si = get_strinfo (idx))
     {
-      gimple *use_stmt = USE_STMT (use_p);
+      if (!si->nonzero_chars)
+	arg = si->ptr;
+      else if (tree_fits_uhwi_p (si->nonzero_chars))
+	{
+	  lenrng[0] = tree_to_uhwi (si->nonzero_chars);
+	  *nulterm = si->full_string_p;
+	  /* Set the upper bound only if the string is known to be
+	     nul-terminated, otherwise leave it at maximum + 1.  */
+	  if (*nulterm)
+	    lenrng[1] = lenrng[0];
+	}
+      else if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
+	{
+	  wide_int min, max;
+	  value_range_kind rng = get_range_info (si->nonzero_chars, &min, &max);
+	  if (rng == VR_RANGE)
+	    {
+	      lenrng[0] = min.to_uhwi ();
+	      lenrng[1] = max.to_uhwi ();
+	      *nulterm = si->full_string_p;
+	    }
+	}
+      else if (si->ptr)
+	arg = si->ptr;
+    }
 
-      if (is_gimple_debug (use_stmt))
-        continue;
-      if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
+  if (lenrng[0] == HOST_WIDE_INT_MAX)
+    {
+      /* Compute the minimum and maximum real or possible lengths.  */
+      c_strlen_data lendata = { };
+      if (get_range_strlen (arg, &lendata, /* eltsize = */1))
 	{
-	  tree_code code = gimple_assign_rhs_code (use_stmt);
-	  if (code == COND_EXPR)
+	  if (tree_fits_shwi_p (lendata.maxlen) && !lendata.maxbound)
 	    {
-	      tree cond_expr = gimple_assign_rhs1 (use_stmt);
-	      if ((TREE_CODE (cond_expr) != EQ_EXPR
-		   && (TREE_CODE (cond_expr) != NE_EXPR))
-		  || !integer_zerop (TREE_OPERAND (cond_expr, 1)))
-		return false;
+	      lenrng[0] = tree_to_shwi (lendata.minlen);
+	      lenrng[1] = tree_to_shwi (lendata.maxlen);
+	      *nulterm = true;
 	    }
-	  else if (code == EQ_EXPR || code == NE_EXPR)
+	  else if (lendata.maxbound && tree_fits_shwi_p (lendata.maxbound))
 	    {
-	      if (!integer_zerop (gimple_assign_rhs2 (use_stmt)))
-		return false;
-            }
-	  else
-	    return false;
+	      /* Set *SIZE to the conservative LENDATA.MAXBOUND which
+		 is a conservative estimate of the longest string based
+		 on the sizes of the arrays referenced by ARG.  */
+	      *size = tree_to_uhwi (lendata.maxbound) + 1;
+	      *nulterm = false;
+	    }
 	}
-      else if (gimple_code (use_stmt) == GIMPLE_COND)
+      else
 	{
-	  tree_code code = gimple_cond_code (use_stmt);
-	  if ((code != EQ_EXPR && code != NE_EXPR)
-	      || !integer_zerop (gimple_cond_rhs (use_stmt)))
-	    return false;
+	  /* Set *SIZE to the size of the smallest object referenced
+	     by ARG if ARG denotes a single object, or to HWI_M1U
+	     otherwise.  */
+	  *size = determine_min_objsize (arg);
+	  *nulterm = false;
 	}
+    }
+
+  return lenrng[0] != HOST_WIDE_INT_MAX || *size != HOST_WIDE_INT_M1U;
+}
+
+/* If IDX1 and IDX2 refer to strings A and B of unequal lengths, return
+   the result of 0 == strncmp (A, B, BOUND) (which is the same as strcmp
+   for s sufficiently large BOUND).  If the result is based on the length
+   of one string being greater than the longest string that would fit in
+   the array pointer to by the argument, set *PLEN and *PSIZE to
+   the corresponding length (or its complement when the string is known
+   to be at least as long and need not be nul-terminated) and size.
+   Otherwise return null.  */
+
+static tree
+strxcmp_eqz_result (tree arg1, int idx1, tree arg2, int idx2,
+		    unsigned HOST_WIDE_INT bound, unsigned HOST_WIDE_INT len[2],
+		    unsigned HOST_WIDE_INT *psize)
+{
+  /* Determine the range the length of each string is in and whether it's
+     known to be nul-terminated, or the size of the array it's stored in.  */
+  bool nul1, nul2;
+  unsigned HOST_WIDE_INT siz1, siz2;
+  unsigned HOST_WIDE_INT len1rng[2], len2rng[2];
+  if (!get_len_or_size (arg1, idx1, len1rng, &siz1, &nul1)
+      || !get_len_or_size (arg2, idx2, len2rng, &siz2, &nul2))
+    return NULL_TREE;
+
+  /* BOUND is set to HWI_M1U for strcmp and less to strncmp, and LENiRNG
+     to HWI_MAX when invalid.  Adjust the length of each string to consider
+     to be no more than BOUND.  */
+  if (len1rng[0] < HOST_WIDE_INT_MAX && len1rng[0] > bound)
+    len1rng[0] = bound;
+  if (len1rng[1] < HOST_WIDE_INT_MAX && len1rng[1] > bound)
+    len1rng[1] = bound;
+  if (len2rng[0] < HOST_WIDE_INT_MAX && len2rng[0] > bound)
+    len2rng[0] = bound;
+  if (len2rng[1] < HOST_WIDE_INT_MAX && len2rng[1] > bound)
+    len2rng[1] = bound;
+
+  /* Two empty strings are equal.  */
+  if (len1rng[1] == 0 && len2rng[1] == 0)
+    return integer_one_node;
+
+  /* The strings are definitely unequal when the lower bound of the length
+     of one of them is greater than the length of the longest string that
+     would fit into the other array.  */
+  if (len1rng[0] == HOST_WIDE_INT_MAX
+      && len2rng[0] != HOST_WIDE_INT_MAX
+      && ((len2rng[0] < bound && len2rng[0] >= siz1)
+	  || len2rng[0] > siz1))
+    {
+      *psize = siz1;
+      len[0] = len1rng[0];
+      /* Set LEN[0] to the lower bound of ARG1's length when it's
+	 nul-terminated or to the complement of its minimum length
+	 otherwise,  */
+      len[1] = nul2 ? len2rng[0] : ~len2rng[0];
+      return integer_zero_node;
+    }
+
+  if (len2rng[0] == HOST_WIDE_INT_MAX
+      && len1rng[0] != HOST_WIDE_INT_MAX
+      && ((len1rng[0] < bound && len1rng[0] >= siz2)
+	  || len1rng[0] > siz2))
+    {
+      *psize = siz2;
+      len[0] = nul1 ? len1rng[0] : ~len1rng[0];
+      len[1] = len2rng[0];
+      return integer_zero_node;
+    }
+
+  /* The strings are also definitely unequal when their lengths are unequal
+     and at least one is nul-terminated.  */
+  if (len1rng[0] != HOST_WIDE_INT_MAX
+      && len2rng[0] != HOST_WIDE_INT_MAX
+      && ((len1rng[1] < len2rng[0] && nul1)
+	  || (len2rng[1] < len1rng[0] && nul2)))
+    {
+      if (bound <= len1rng[0] || bound <= len2rng[0])
+	*psize = bound;
       else
-        return false;
+	*psize = HOST_WIDE_INT_M1U;
+
+      len[0] = len1rng[0];
+      len[1] = len2rng[0];
+      return integer_zero_node;
+    }
+
+  /* The string lengths may be equal or unequal.  Even when equal and
+     both strings nul-terminated, without the string contents there's
+     no way to determine whether they are equal.  */
+  return NULL_TREE;
+}
+
+/* Diagnose pointless calls to strcmp whose result is used in equality
+   epxpressions that evaluate to a constant due to one argument being
+   longer than the size of the other.  */
+
+static void
+maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound,
+			     unsigned HOST_WIDE_INT len[2],
+			     unsigned HOST_WIDE_INT siz)
+{
+  bool at_least = false;
+
+  if (len[0] > HOST_WIDE_INT_MAX)
+    {
+      at_least = true;
+      len[0] = ~len[0];
+    }
+
+  if (len[1] > HOST_WIDE_INT_MAX)
+    {
+      at_least = true;
+      len[1] = ~len[1];
     }
 
-  /* When the lengths of the arguments are known to be unequal
-     we can safely fold the call to a non-zero value for strcmp;
-     otherwise, do nothing now.  */
-  if (idx1 != 0 && idx2 != 0)
+  unsigned HOST_WIDE_INT minlen = MIN (len[0], len[1]);
+
+  tree lhs = gimple_call_lhs (stmt);
+
+  /* FIXME: Include a note pointing to the declaration
+     of the smaller array.  */
+  if (gimple *use = used_only_for_zero_equality (lhs))
     {
-      if (strxcmp_unequal (idx1, idx2, length))
+      location_t stmt_loc = gimple_location (stmt);
+      tree callee = gimple_call_fndecl (stmt);
+      bool warned = false;
+      if (siz <= minlen && bound == -1)
+	warned = warning_at (stmt_loc, OPT_Wstring_compare,
+			     (at_least
+			      ? G_("%G%qD of a string of length %wu "
+				   "or more and an array of size %wu "
+				   "evaluates to nonzero")
+			      : G_("%G%qD of a string of length %wu "
+				   "and an array of size %wu "
+				   "evaluates to nonzero")),
+			     stmt, callee, minlen, siz);
+      else if (!at_least && siz <= HOST_WIDE_INT_MAX)
 	{
-	  replace_call_with_value (gsi, integer_one_node);
-	  return true;
+	  if (len[0] != HOST_WIDE_INT_MAX
+	      && len[1] != HOST_WIDE_INT_MAX)
+	    warned = warning_at (stmt_loc, OPT_Wstring_compare,
+				 "%G%qD of strings of length %wu "
+				 "and %wu and bound of %wu evaluates "
+				 "to nonzero",
+				 stmt, callee, len[0], len[1], bound);
+	  else
+	    warned = warning_at (stmt_loc, OPT_Wstring_compare,
+				 "%G%qD of a string of length %wu, "
+				 "an array of size %wu and bound "
+				 "of %wu evaluates to nonzero",
+				 stmt, callee, minlen, siz, bound);
+	}
+
+      if (warned)
+	{
+	  location_t use_loc = gimple_location (use);
+	  if (LOCATION_LINE (stmt_loc) != LOCATION_LINE (use_loc))
+	    inform (use_loc, "in this expression");
 	}
-      return false;
     }
+}
 
-  /* When the length of one argument is constant.  */
-  tree var_string = NULL_TREE;
-  HOST_WIDE_INT const_string_leni = -1;
 
-  if (idx1)
+/* Optimize a call to strcmp or strncmp either by folding it to a constant
+   when possible or by transforming the latter to the former.  Warn about
+   calls where the length of one argument is greater than the size of
+   the array to which the other aargument points if the latter's length
+   is not known.  Return true when the call has been transformed into
+   another and false otherwise.  */
+
+static bool
+handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree lhs = gimple_call_lhs (stmt);
+
+  if (!lhs)
+    return false;
+
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  int idx1 = get_stridx (arg1);
+  int idx2 = get_stridx (arg2);
+
+  /* For strncmp set to the the value of the third argument if known.  */
+  HOST_WIDE_INT bound = -1;
+
+  /* Extract the strncmp bound.  */
+  if (gimple_call_num_args (stmt) == 3)
     {
-      const_string_leni = compute_string_length (idx1);
-      var_string = arg2;
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_shwi_p (len))
+        bound = tree_to_shwi (len);
+
+      /* If the bound argument is NOT known, do nothing.  */
+      if (bound < 0)
+	return false;
     }
-  else
+
+  /* Set to the length of one argument (or its complement if it's
+     the lower bound of a range) and the size of the array storing
+     the other if the result is based on the former being equal to
+     or greater than the latter.  */
+  unsigned HOST_WIDE_INT len[2] = { HOST_WIDE_INT_MAX, HOST_WIDE_INT_MAX };
+  unsigned HOST_WIDE_INT siz = HOST_WIDE_INT_M1U;
+
+  /* Try to determine if the two strings are either definitely equal
+     or definitely unequal and if so, either fold the result to zero
+     (when equal) or set the range of the result to ~[0, 0] otherwise.  */
+  if (tree eqz = strxcmp_eqz_result (arg1, idx1, arg2, idx2, bound,
+				     len, &siz))
     {
-      gcc_checking_assert (idx2);
-      const_string_leni = compute_string_length (idx2);
-      var_string = arg1;
+      if (integer_zerop (eqz))
+	{
+	  maybe_warn_pointless_strcmp (stmt, bound, len, siz);
+
+	  if (bound < 0)
+	    inform (gimple_location (stmt),
+		    "%G%qD (%qE, %qE) set result to ~[0, 0]",
+		    (gimple *)stmt, gimple_call_fndecl (stmt), arg1, arg2);
+	  else
+	    inform (gimple_location (stmt),
+		    "%G%qD (%qE, %qE, %wu) set result to ~[0, 0]",
+		    (gimple *)stmt, gimple_call_fndecl (stmt), arg1, arg2, bound);
+	  /* When the lengths of the first two string arguments are
+	     known to be unequal set the range of the result to non-zero.
+	     This allows the call to be eliminated if its result is only
+	     used in tests for equality to zero.  */
+	  wide_int zero = wi::zero (TYPE_PRECISION (TREE_TYPE (lhs)));
+	  set_range_info (lhs, VR_ANTI_RANGE, zero, zero);
+	  return false;
+	}
+      /* When the two strings are definitely equal (such as when they
+	 are both empty) fold the call to the constant result.  */
+      replace_call_with_value (gsi, integer_zero_node);
+      if (bound < 0)
+	inform (gimple_location (stmt),
+		"%G%qD (%qE, %qE) folded to zero",
+		(gimple *)stmt, gimple_call_fndecl (stmt), arg1, arg2);
+      else
+	inform (gimple_location (stmt),
+		"%G%qD (%qE, %qE, %wu) folded to zero",
+		(gimple *)stmt, gimple_call_fndecl (stmt), arg1, arg2, bound);
+      return true;
     }
 
-  if (const_string_leni < 0)
+  if (idx1 == 0 && idx2 == 0)
     return false;
 
-  unsigned HOST_WIDE_INT var_sizei = 0;
-  /* try to determine the minimum size of the object pointed by var_string.  */
-  tree size = determine_min_objsize (var_string);
+  /* Determine either the length or the size of each of the string
+     orguments, whichever is available.  */
+  HOST_WIDE_INT cstlen1 = -1, cstlen2 = -1;
+  HOST_WIDE_INT arysiz1 = -1, arysiz2 = -1;
+
+  if (idx1)
+    cstlen1 = compute_string_length (idx1) + 1;
+  else
+    arysiz1 = determine_min_objsize (arg1);
 
-  if (!size)
+  /* Bail if neither the string length nor the size of the array
+     it is stored in can be determined.  */
+  if (cstlen1 < 0 && arysiz1 < 0)
     return false;
 
-  if (tree_fits_uhwi_p (size))
-    var_sizei = tree_to_uhwi (size);
+  /* Repeat for the second argument.  */
+  if (idx2)
+    cstlen2 = compute_string_length (idx2) + 1;
+  else
+    arysiz2 = determine_min_objsize (arg2);
 
-  if (var_sizei == 0)
+  if (cstlen2 < 0 && arysiz2 < 0)
     return false;
 
-  /* For strncmp, if length > const_string_leni , this call can be safely
-     transformed to a strcmp.  */
-  if (is_ncmp && length > const_string_leni)
-    is_ncmp = false;
-
-  unsigned HOST_WIDE_INT final_length
-    = is_ncmp ? length : const_string_leni + 1;
+  /* The exact number of characters to compare.  */
+  HOST_WIDE_INT cmpsiz = bound < 0 ? cstlen1 < 0 ? cstlen2 : cstlen1 : bound;
+  /* The size of the array in which the unknown string is stored.  */
+  HOST_WIDE_INT varsiz = arysiz1 < 0 ? arysiz2 : arysiz1;
 
-  /* Replace strcmp or strncmp with the corresponding str(n)cmp_eq.  */
-  if (var_sizei > final_length)
+  if (cmpsiz < varsiz && used_only_for_zero_equality (lhs))
     {
-      tree fn
-	= (is_ncmp
-	   ? builtin_decl_implicit (BUILT_IN_STRNCMP_EQ)
-	   : builtin_decl_implicit (BUILT_IN_STRCMP_EQ));
-      if (!fn)
-	return false;
-      tree const_string_len = build_int_cst (size_type_node, final_length);
-      update_gimple_call (gsi, fn, 3, arg1, arg2, const_string_len);
+      /* If the known length is less than the size of the other array
+	 and the strcmp result is only used to test equality to zero,
+	 transform the call to the equivalent _eq call.  */
+      if (tree fn = builtin_decl_implicit (bound < 0 ? BUILT_IN_STRCMP_EQ
+					   : BUILT_IN_STRNCMP_EQ))
+	{
+	  if (bound < 0)
+	    inform (gimple_location (stmt),
+		    "%G%qD (%qE, %qE) transformed to %qD (..., %wi)",
+		    (gimple *)stmt, gimple_call_fndecl (stmt), arg1, arg2,
+		    fn, cmpsiz);
+	  else
+	    inform (gimple_location (stmt),
+		    "%G%qD (%qE, %qE, %wu) transformed to %qD (..., %wi)",
+		    (gimple *)stmt,
+		    gimple_call_fndecl (stmt), arg1, arg2, bound,
+		    fn, cmpsiz);
+	  tree n = build_int_cst (size_type_node, cmpsiz);
+	  update_gimple_call (gsi, fn, 3, arg1, arg2, n);
+	  return true;
+	}
     }
-  else
-    return false;
 
-  return true;
+  return false;
 }
 
 /* Handle a POINTER_PLUS_EXPR statement.


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

end of thread, other threads:[~2019-10-04 21:15 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-09 16:42 [PATCH] fold more string comparison with known result (PR 90879) Martin Sebor
2019-08-09 16:51 ` Jakub Jelinek
2019-08-09 17:07   ` Martin Sebor
2019-08-09 17:07     ` Jakub Jelinek
2019-08-09 22:45       ` Martin Sebor
2019-08-12 13:56         ` Michael Matz
2019-08-14 16:30           ` Martin Sebor
2019-08-12 20:15         ` Jeff Law
2019-08-12 22:32           ` Martin Sebor
2019-08-13  2:22             ` Jeff Law
2019-08-13 20:08     ` Jeff Law
2019-08-13 23:26       ` Martin Sebor
2019-08-14  0:39         ` Jeff Law
2019-08-14 20:57           ` Martin Sebor
2019-08-21  7:40             ` Martin Sebor
2019-08-22 22:23               ` Jeff Law
2019-08-28 21:36                 ` Martin Sebor
2019-09-03 20:01                   ` Jeff Law
2019-09-23 22:14                     ` Martin Sebor
2019-10-04 21:15                       ` Jeff Law
2019-08-12 22:22 ` Jeff Law

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).