PR tree-optimization/92501 - strncmp with constant unterminated arrays not folded
authorMartin Sebor <msebor@redhat.com>
Fri, 22 Nov 2019 16:39:37 +0000 (16:39 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Fri, 22 Nov 2019 16:39:37 +0000 (09:39 -0700)
gcc/testsuite/ChangeLog:

PR tree-optimization/92501
* gcc.dg/strcmpopt_7.c: New test.

gcc/ChangeLog:

PR tree-optimization/92501
* gimple-fold.c ((gimple_fold_builtin_string_compare): Let strncmp
handle unterminated arrays.  Rename local variables for clarity.

From-SVN: r278621

gcc/ChangeLog
gcc/gimple-fold.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/strcmpopt_7.c [new file with mode: 0644]

index 2512838bc3efc99ebb5f46d42f266966e697142a..9fbe728baf0150ecaab8dbb329e600cc86b8ab36 100644 (file)
@@ -1,3 +1,9 @@
+2019-11-22  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/92501
+       * gimple-fold.c ((gimple_fold_builtin_string_compare): Let strncmp
+       handle unterminated arrays.  Rename local variables for clarity.
+
 2019-11-22  Andrew Stubbs  <ams@codesourcery.com>
 
        * config/gcn/gcn.c (gcn_hsa_declare_function_name): Calculate
index e176be80efbf305312c09a0f9e00f2e2e382a7e5..cfae9db60432a6f914c6195eda7003d84d927445 100644 (file)
@@ -2323,8 +2323,7 @@ gimple_load_first_char (location_t loc, tree str, gimple_seq *stmts)
   return var;
 }
 
-/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
-   FCODE is the name of the builtin.  */
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.  */
 
 static bool
 gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
@@ -2337,18 +2336,19 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
   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;
+  tree len = NULL_TREE;
+  unsigned HOST_WIDE_INT bound = HOST_WIDE_INT_M1U;
 
   /* Handle strncmp and strncasecmp functions.  */
   if (gimple_call_num_args (stmt) == 3)
     {
-      tree len = gimple_call_arg (stmt, 2);
+      len = gimple_call_arg (stmt, 2);
       if (tree_fits_uhwi_p (len))
-       length = tree_to_uhwi (len);
+       bound = tree_to_uhwi (len);
     }
 
   /* If the LEN parameter is zero, return zero.  */
-  if (length == 0)
+  if (bound == 0)
     {
       replace_call_with_value (gsi, integer_zero_node);
       return true;
@@ -2361,8 +2361,32 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
       return true;
     }
 
-  const char *p1 = c_getstr (str1);
-  const char *p2 = c_getstr (str2);
+  /* Initially set to the number of characters, including the terminating
+     nul if each array has one.   LENx == strnlen (Sx, LENx) implies that
+     the array Sx is not terminated by a nul.
+     For nul-terminated strings then adjusted to their length so that
+     LENx == NULPOSx holds.  */
+  unsigned HOST_WIDE_INT len1 = HOST_WIDE_INT_MAX, len2 = len1;
+  const char *p1 = c_getstr (str1, &len1);
+  const char *p2 = c_getstr (str2, &len2);
+
+  /* The position of the terminating nul character if one exists, otherwise
+     a value greater than LENx.  */
+  unsigned HOST_WIDE_INT nulpos1 = HOST_WIDE_INT_MAX, nulpos2 = nulpos1;
+
+  if (p1)
+    {
+      size_t n = strnlen (p1, len1);
+      if (n < len1)
+       len1 = nulpos1 = n;
+    }
+
+  if (p2)
+    {
+      size_t n = strnlen (p2, len2);
+      if (n < len2)
+       len2 = nulpos2 = n;
+    }
 
   /* For known strings, return an immediate value.  */
   if (p1 && p2)
@@ -2374,17 +2398,30 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
        {
        case BUILT_IN_STRCMP:
        case BUILT_IN_STRCMP_EQ:
-         {
-           r = strcmp (p1, p2);
-           known_result = true;
+         if (len1 != nulpos1 || len2 != nulpos2)
            break;
-         }
+
+         r = strcmp (p1, p2);
+         known_result = true;
+         break;
+
        case BUILT_IN_STRNCMP:
        case BUILT_IN_STRNCMP_EQ:
          {
-           if (length == -1)
+           /* Reduce the bound to be no more than the length
+              of the shorter of the two strings, or the sizes
+              of the unterminated arrays.  */
+           unsigned HOST_WIDE_INT n = bound;
+
+           if (len1 == nulpos1 && len1 < n)
+             n = len1 + 1;
+           if (len2 == nulpos2 && len2 < n)
+             n = len2 + 1;
+
+           if (MIN (nulpos1, nulpos2) + 1 < n)
              break;
-           r = strncmp (p1, p2, length);
+
+           r = strncmp (p1, p2, n);
            known_result = true;
            break;
          }
@@ -2394,9 +2431,9 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
          break;
        case BUILT_IN_STRNCASECMP:
          {
-           if (length == -1)
+           if (bound == HOST_WIDE_INT_M1U)
              break;
-           r = strncmp (p1, p2, length);
+           r = strncmp (p1, p2, bound);
            if (r == 0)
              known_result = true;
            break;
@@ -2412,7 +2449,7 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
        }
     }
 
-  bool nonzero_length = length >= 1
+  bool nonzero_bound = (bound >= 1 && bound < HOST_WIDE_INT_M1U)
     || fcode == BUILT_IN_STRCMP
     || fcode == BUILT_IN_STRCMP_EQ
     || fcode == BUILT_IN_STRCASECMP;
@@ -2420,7 +2457,7 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
   location_t loc = gimple_location (stmt);
 
   /* If the second arg is "", return *(const unsigned char*)arg1.  */
-  if (p2 && *p2 == '\0' && nonzero_length)
+  if (p2 && *p2 == '\0' && nonzero_bound)
     {
       gimple_seq stmts = NULL;
       tree var = gimple_load_first_char (loc, str1, &stmts);
@@ -2435,7 +2472,7 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
     }
 
   /* If the first arg is "", return -*(const unsigned char*)arg2.  */
-  if (p1 && *p1 == '\0' && nonzero_length)
+  if (p1 && *p1 == '\0' && nonzero_bound)
     {
       gimple_seq stmts = NULL;
       tree var = gimple_load_first_char (loc, str2, &stmts);
@@ -2454,9 +2491,9 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
       return true;
     }
 
-  /* If len parameter is one, return an expression corresponding to
+  /* If BOUND is one, return an expression corresponding to
      (*(const unsigned char*)arg2 - *(const unsigned char*)arg1).  */
-  if (fcode == BUILT_IN_STRNCMP && length == 1)
+  if (fcode == BUILT_IN_STRNCMP && bound == 1)
     {
       gimple_seq stmts = NULL;
       tree temp1 = gimple_load_first_char (loc, str1, &stmts);
@@ -2480,12 +2517,13 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
       return true;
     }
 
-  /* If length is larger than the length of one constant string, 
-     replace strncmp with corresponding strcmp */ 
-  if (fcode == BUILT_IN_STRNCMP 
-      && length > 0
-      && ((p2 && (size_t) length > strlen (p2)) 
-          || (p1 && (size_t) length > strlen (p1))))
+  /* If BOUND is greater than the length of one constant string,
+     and the other argument is also a nul-terminated string, replace
+     strncmp with strcmp.  */
+  if (fcode == BUILT_IN_STRNCMP
+      && bound > 0 && bound < HOST_WIDE_INT_M1U
+      && ((p2 && len2 < bound && len2 == nulpos2)
+         || (p1 && len1 < bound && len1 == nulpos1)))
     {
       tree fn = builtin_decl_implicit (BUILT_IN_STRCMP);
       if (!fn)
index ac0b1ad22adc9400afc9c473816a358a6d49d7ca..f602c9b27ad7242584c15315bafd2453fbd68d71 100644 (file)
@@ -1,3 +1,8 @@
+2019-11-22  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/92501
+       * gcc.dg/strcmpopt_7.c: New test.
+
 2019-11-22  Richard Sandiford  <richard.sandiford@arm.com>
 
        * gcc.dg/vect/vect-widen-mult-u8.c: Disable epilogue loop
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_7.c b/gcc/testsuite/gcc.dg/strcmpopt_7.c
new file mode 100644 (file)
index 0000000..5e71317
--- /dev/null
@@ -0,0 +1,119 @@
+/* PR tree-optimization/92501 - strncmp with constant unterminated arrays
+   not folded
+   { dg-do compile }
+   { dg-options "-O1 -Wall -fdump-tree-forwprop1" } */
+
+/* Unterminated arrays of the size encoded in name.  */
+const char a1[] = { '1' };
+const char a12[] = { '1', '2' };
+const char a112[] = { '1', '1', '2' };
+const char a123[] = { '1', '2', '3' };
+
+/* Nul-terminated strings of the length encoded in name.  */
+const char s[] = "";
+const char s1[] = "1";
+const char s12[] = "12";
+const char s112[] = "112";
+const char s123[] = "123";
+
+extern void failure_on_line (int);
+
+/* Verify that the test in 'if (EQL strncmp (S, T, N))' is folded.  */
+#define T(eql, s, t, n) do {                   \
+    if (!(eql __builtin_strncmp (s, t, n)))    \
+      failure_on_line (__LINE__);              \
+  } while (0)
+
+
+void test (void)
+{
+  /* Mixed array and string.  */
+  T (0 ==, a1, "", 0);
+  T (0 ==, a1, s, 0);
+  T (0 !=, a1, "", 1);
+  T (0 !=, a1, s, 1);
+
+  /* The following two are safe to fold because while strncmp compares
+     at most N bytes it doesn't compare any bytes past the first nul.  */
+  T (0 !=, a1, "", 9);
+  T (0 !=, a1, s, 9);
+
+  T (0 ==, a1, "1", 0);
+  T (0 ==, a1, s1, 0);
+  T (0 ==, a1, "1", 1);
+  T (0 ==, a1, s1, 1);
+  T (0 ==, a1, "12", 1);
+  T (0 ==, a1, s12, 1);
+
+     /* As above, the following three are also safe to fold.  */
+  T (0 !=, a1, s12 + 1, 1);
+  T (0 !=, a1, s12 + 1, 2);
+  T (0 !=, a1, s12 + 1, 9);
+
+  T (0 ==, a12, s, 0);
+  T (0 ==, a12, "", 0);
+  T (0 ==, a12, s1, 0);
+  T (0 ==, a12, "1", 0);
+  T (0 ==, a12, s1, 1);
+  T (0 ==, a12, "1", 1);
+  T (0 !=, a12, s1, 2);
+  T (0 !=, a12, "1", 2);
+  T (0 ==, a12, s12, 0);
+  T (0 ==, a12, "12", 0);
+  T (0 ==, a12, s12, 1);
+  T (0 ==, a12, "12", 1);
+  T (0 ==, a12, s12, 2);
+  T (0 ==, a12, "12", 2);
+  T (0 ==, a12, s123, 2);
+  T (0 ==, a12, "123", 2);
+
+  T (0 ==, a12 + 0, s123 + 1, 0);
+  T (0 !=, a12 + 0, s123 + 1, 1);
+  T (0 !=, a12 + 0, s123 + 1, 2);
+  T (0 ==, a12 + 1, s123 + 0, 0);
+  T (0 !=, a12 + 1, s123 + 0, 1);
+  T (0 !=, a12 + 1, s123 + 0, 2);
+  T (0 ==, a12 + 1, s123 + 1, 1);
+  T (0 !=, a12 + 1, s123 + 2, 1);
+  T (0 !=, a12 + 1, s123 + 3, 1);
+
+  T (0 ==, a12 + 1, "123" + 1, 1);
+  T (0 !=, a12 + 1, "123" + 2, 1);
+  T (0 !=, a12 + 1, "123" + 3, 1);
+  T (0 !=, a12 + 1, "123" + 3, 9);
+
+  /* Both arguments arrays.  */
+  T (0 ==, a112 + 0, a1, 1);
+  T (0 ==, a112 + 1, a1, 1);
+  T (0 !=, a112 + 2, a1, 1);
+
+  T (0 ==, a1, a112 + 0, 1);
+  T (0 ==, a1, a112 + 1, 1);
+  T (0 !=, a1, a112 + 2, 1);
+
+  T (0 ==, a112 + 0, a12, 0);
+  T (0 ==, a112 + 0, a12, 1);
+  T (0 !=, a112 + 0, a12, 2);
+
+  T (0 ==, a112 + 1, a12, 2);
+  T (0 !=, a112 + 1, a12 + 1, 1);
+  T (0 ==, a112 + 2, a12 + 1, 1);
+
+  /* Mixed array and string.  */
+  T (0 ==, s112 + 0, a12, 0);
+  T (0 ==, s112 + 0, a12, 1);
+  T (0 !=, s112 + 0, a12, 2);
+
+  T (0 ==, s112 + 1, a12, 0);
+  T (0 ==, s112 + 1, a12, 1);
+  T (0 ==, s112 + 1, a12, 2);
+  T (0 !=, s112 + 2, a12, 2);
+
+  T (0 ==, a112 + 0, s1, 1);
+  T (0 ==, a112 + 1, s1, 1);
+  T (0 !=, a112 + 2, s1, 1);
+}
+
+/* { dg-final { scan-tree-dump-not "strcmp" "forwprop1" } }
+   { dg-final { scan-tree-dump-not "strncmp" "forwprop1" } }
+   { dg-final { scan-tree-dump-not "failure_on_line_" "forwprop1" } } */