2nd Patch for PR78009
authorQing Zhao <qing.zhao@oracle.com>
Thu, 31 May 2018 20:01:42 +0000 (20:01 +0000)
committerQing Zhao <qinzhao@gcc.gnu.org>
Thu, 31 May 2018 20:01:42 +0000 (20:01 +0000)
Patch for PR83026

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78809
Inline strcmp with small constant strings

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83026
missing strlen optimization for strcmp of unequal strings

The design doc for PR78809 is at:
https://www.mail-archive.com/gcc@gcc.gnu.org/msg83822.html

this patch is for the second part of change of PR78809 and PR83026:

B. for strncmp (s1, s2, n) (!)= 0 or strcmp (s1, s2) (!)= 0

   B.1. (PR83026) 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.2. (PR78809) 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
         __builtin_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
         __builtin_strcmp_eq (s, STR, strlen(STR)+1) (!)= 0
       }

   later when expanding the new __builtin_str(n)cmp_eq calls, first expand them
   as __builtin_memcmp_eq, if the expansion does not succeed, change them back
   to call to __builtin_str(n)cmp.

adding test case strcmpopt_2.c and strcmpopt_4.c into gcc.dg for part B of
PR78809 adding test case strcmpopt_3.c into gcc.dg for PR83026

From-SVN: r261039

gcc/ChangeLog
gcc/builtins.c
gcc/builtins.def
gcc/gimple-fold.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/strcmpopt_2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/strcmpopt_3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/strcmpopt_4.c [new file with mode: 0644]
gcc/tree-ssa-strlen.c
gcc/tree-ssa-structalias.c
gcc/tree.c

index 09c8b7cdd9423936069a3e0318d1575c09e19948..370344c66e6dd299bfa57feac63a61e5252f806c 100644 (file)
@@ -1,3 +1,25 @@
+2018-05-31  Qing Zhao <qing.zhao@oracle.com>
+
+       PR middle-end/78809
+       PR middle-end/83026
+       * builtins.c (expand_builtin): Add the handling of BUILT_IN_STRCMP_EQ
+       and BUILT_IN_STRNCMP_EQ.
+       * builtins.def: Add new builtins BUILT_IN_STRCMP_EQ and
+       BUILT_IN_STRNCMP_EQ.
+       * gimple-fold.c (gimple_fold_builtin_string_compare): Add the
+       handling of BUILTIN_IN_STRCMP_EQ and BUILT_IN_STRNCMP_EQ.
+       (gimple_fold_builtin): Likewise.
+       * tree-ssa-strlen.c (compute_string_length): New function.
+       (determine_min_obsize): New function.
+       (handle_builtin_string_cmp): New function to handle calls to
+       string compare functions.
+       (strlen_optimize_stmt): Add handling to builtin string compare
+       calls.
+       * tree-ssa-structalias.c (find_func_aliases_for_builtin_call):
+       Add the handling of BUILT_IN_STRCMP_EQ and BUILT_IN_STRNCMP_EQ.
+       * tree.c (build_common_builtin_nodes): Add new defines of
+       BUILT_IN_STRNCMP_EQ and BUILT_IN_STRCMP_EQ.
+
 2018-05-31  Jakub Jelinek  <jakub@redhat.com>
 
        PR target/85984
index 97419399baeaf02356c97a69b8b617ad5f661778..c96ac38e8e9adc1ba1f496e5c89a79405882fdac 100644 (file)
@@ -7139,12 +7139,45 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
        return target;
       break;
 
+    /* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it 
+       back to a BUILT_IN_STRCMP. Remember to delete the 3rd paramater
+       when changing it to a strcmp call.  */
+    case BUILT_IN_STRCMP_EQ:
+      target = expand_builtin_memcmp (exp, target, true);
+      if (target)
+       return target;
+
+      /* Change this call back to a BUILT_IN_STRCMP.  */
+      TREE_OPERAND (exp, 1) 
+       = build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRCMP));
+
+      /* Delete the last parameter.  */
+      unsigned int i;
+      vec<tree, va_gc> *arg_vec;
+      vec_alloc (arg_vec, 2);
+      for (i = 0; i < 2; i++)
+       arg_vec->quick_push (CALL_EXPR_ARG (exp, i));
+      exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), arg_vec);
+      /* FALLTHROUGH */
+
     case BUILT_IN_STRCMP:
       target = expand_builtin_strcmp (exp, target);
       if (target)
        return target;
       break;
 
+    /* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it
+       back to a BUILT_IN_STRNCMP.  */
+    case BUILT_IN_STRNCMP_EQ:
+      target = expand_builtin_memcmp (exp, target, true);
+      if (target)
+       return target;
+
+      /* Change it back to a BUILT_IN_STRNCMP.  */
+      TREE_OPERAND (exp, 1) 
+       = build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRNCMP));
+      /* FALLTHROUGH */
+
     case BUILT_IN_STRNCMP:
       target = expand_builtin_strncmp (exp, target, mode);
       if (target)
index 449d08d682ff6a513407a473187068644640d6c0..58b469844a7a8b80c50e3359dd649d07f184ac94 100644 (file)
@@ -971,6 +971,11 @@ DEF_BUILTIN_STUB (BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX, "__builtin_alloca_with_ali
    equality with zero.  */
 DEF_BUILTIN_STUB (BUILT_IN_MEMCMP_EQ, "__builtin_memcmp_eq")
 
+/* An internal version of strcmp/strncmp, used when the result is only 
+   tested for equality with zero.  */
+DEF_BUILTIN_STUB (BUILT_IN_STRCMP_EQ, "__builtin_strcmp_eq")
+DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
+
 /* Object size checking builtins.  */
 DEF_GCC_BUILTIN               (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
 DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
index cd1ab8058b4972a1a5d30de9d15b820b9730558e..9bbe7f1d9092c268562059748e81442958ac9c1f 100644 (file)
@@ -2215,12 +2215,14 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
       switch (fcode)
        {
        case BUILT_IN_STRCMP:
+       case BUILT_IN_STRCMP_EQ:
          {
            r = strcmp (p1, p2);
            known_result = true;
            break;
          }
        case BUILT_IN_STRNCMP:
+       case BUILT_IN_STRNCMP_EQ:
          {
            if (length == -1)
              break;
@@ -2254,6 +2256,7 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
 
   bool nonzero_length = length >= 1
     || fcode == BUILT_IN_STRCMP
+    || fcode == BUILT_IN_STRCMP_EQ
     || fcode == BUILT_IN_STRCASECMP;
 
   location_t loc = gimple_location (stmt);
@@ -3687,8 +3690,10 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_STRSTR:
       return gimple_fold_builtin_strstr (gsi);
     case BUILT_IN_STRCMP:
+    case BUILT_IN_STRCMP_EQ:
     case BUILT_IN_STRCASECMP:
     case BUILT_IN_STRNCMP:
+    case BUILT_IN_STRNCMP_EQ:
     case BUILT_IN_STRNCASECMP:
       return gimple_fold_builtin_string_compare (gsi);
     case BUILT_IN_MEMCHR:
index e0745e08fc812291d154f119fd80be3c22479c21..677d8e00d18ea8179bb43f371b1911f70e34ad03 100644 (file)
@@ -1,3 +1,12 @@
+2018-05-31  Qing Zhao <qing.zhao@oracle.com>
+
+       PR middle-end/78809
+       * gcc.dg/strcmpopt_2.c: New test.
+       * gcc.dg/strcmpopt_3.c: New test.
+
+       PR middle-end/83026
+       * gcc.dg/strcmpopt_3.c: New test.
+
 2018-05-31  Jakub Jelinek  <jakub@redhat.com>
 
        PR target/85984
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_2.c b/gcc/testsuite/gcc.dg/strcmpopt_2.c
new file mode 100644 (file)
index 0000000..0131b8f
--- /dev/null
@@ -0,0 +1,67 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+char s[100] = {'a','b','c','d'};
+typedef struct { char s[8]; int x; } S;
+
+__attribute__ ((noinline)) int 
+f1 (S *s) 
+{ 
+  return __builtin_strcmp (s->s, "abc") != 0; 
+}
+
+__attribute__ ((noinline)) int 
+f2 (void) 
+{ 
+  return __builtin_strcmp (s, "abc") != 0; 
+}
+
+__attribute__ ((noinline)) int 
+f3 (S *s) 
+{ 
+  return __builtin_strcmp ("abc", s->s) != 0; 
+}
+
+__attribute__ ((noinline)) int 
+f4 (void) 
+{ 
+  return __builtin_strcmp ("abc", s) != 0; 
+}
+
+__attribute__ ((noinline)) int 
+f5 (S *s) 
+{ 
+  return __builtin_strncmp (s->s, "abc", 3) != 0; 
+}
+
+__attribute__ ((noinline)) int 
+f6 (void) 
+{ 
+  return __builtin_strncmp (s, "abc", 2) != 0; 
+}
+
+__attribute__ ((noinline)) int 
+f7 (S *s) 
+{ 
+  return __builtin_strncmp ("abc", s->s, 3) != 0; 
+}
+
+__attribute__ ((noinline)) int 
+f8 (void) 
+{ 
+  return __builtin_strncmp ("abc", s, 2) != 0; 
+}
+
+int main (void)
+{
+  S ss = {{'a','b','c'}, 2};
+
+  if (f1 (&ss) != 0 || f2 () != 1 || f3 (&ss) != 0 ||
+      f4 () != 1 || f5 (&ss) != 0 || f6 () != 0 ||
+      f7 (&ss) != 0 || f8 () != 0)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_3.c b/gcc/testsuite/gcc.dg/strcmpopt_3.c
new file mode 100644 (file)
index 0000000..86a0d7a
--- /dev/null
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+__attribute__ ((noinline)) int 
+f1 (void) 
+{ 
+  char *s0= "abcd";
+  char s[8];
+  __builtin_strcpy (s, s0);
+  return __builtin_strcmp(s, "abc") != 0; 
+}
+
+__attribute__ ((noinline)) int
+f2 (void) 
+{ 
+  char *s0 = "ab";
+  char s[8];
+  __builtin_strcpy (s, s0);
+  return __builtin_strcmp("abc", s) != 0; 
+}
+
+int main (void)
+{
+  if (f1 () != 1 
+      || f2 () != 1)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_4.c b/gcc/testsuite/gcc.dg/strcmpopt_4.c
new file mode 100644 (file)
index 0000000..d727bc3
--- /dev/null
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+typedef struct { char s[8]; int x; } S;
+extern int max_i;
+
+int
+f1 (S * s)
+{ 
+  int result, i;
+  for (i = 0; i < max_i; i++)
+    result += __builtin_strcmp (s->s, "abc") != 0 ? 2 : 1;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen" } } */
index 556c5bc29fedd4216290cf79dfd6627c82f5163c..7a89174d6aaa45bac9c9ce8575a41061365fad41 100644 (file)
@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "params.h"
 #include "ipa-chkp.h"
 #include "tree-hash-traits.h"
+#include "tree-object-size.h"
 #include "builtins.h"
 #include "target.h"
 #include "diagnostic-core.h"
@@ -2627,27 +2628,28 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
 /* Handle a call to memset.
    After a call to calloc, memset(,0,) is unnecessary.
-   memset(malloc(n),0,n) is calloc(n,1).  */
+   memset(malloc(n),0,n) is calloc(n,1). 
+   return true when the call is transfomred, false otherwise.  */
 
 static bool
 handle_builtin_memset (gimple_stmt_iterator *gsi)
 {
   gimple *stmt2 = gsi_stmt (*gsi);
   if (!integer_zerop (gimple_call_arg (stmt2, 1)))
-    return true;
+    return false;
   tree ptr = gimple_call_arg (stmt2, 0);
   int idx1 = get_stridx (ptr);
   if (idx1 <= 0)
-    return true;
+    return false;
   strinfo *si1 = get_strinfo (idx1);
   if (!si1)
-    return true;
+    return false;
   gimple *stmt1 = si1->stmt;
   if (!stmt1 || !is_gimple_call (stmt1))
-    return true;
+    return false;
   tree callee1 = gimple_call_fndecl (stmt1);
   if (!valid_builtin_call (stmt1))
-    return true;
+    return false;
   enum built_in_function code1 = DECL_FUNCTION_CODE (callee1);
   tree size = gimple_call_arg (stmt2, 2);
   if (code1 == BUILT_IN_CALLOC)
@@ -2663,7 +2665,7 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
       si1->stmt = gsi_stmt (gsi1);
     }
   else
-    return true;
+    return false;
   tree lhs = gimple_call_lhs (stmt2);
   unlink_stmt_vdef (stmt2);
   if (lhs)
@@ -2677,12 +2679,13 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
       release_defs (stmt2);
     }
 
-  return false;
+  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.  */
+   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)
@@ -2697,7 +2700,7 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
   imm_use_iterator iter;
 
   if (!res)
-    return true;
+    return false;
 
   FOR_EACH_IMM_USE_FAST (use_p, iter, res)
     {
@@ -2711,17 +2714,17 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
          tree_code code = gimple_assign_rhs_code (asgn);
          if ((code != EQ_EXPR && code != NE_EXPR)
              || !integer_zerop (gimple_assign_rhs2 (asgn)))
-           return true;
+           return false;
        }
       else if (gimple_code (ustmt) == GIMPLE_COND)
        {
          tree_code code = gimple_cond_code (ustmt);
          if ((code != EQ_EXPR && code != NE_EXPR)
              || !integer_zerop (gimple_cond_rhs (ustmt)))
-           return true;
+           return false;
        }
       else
-       return true;
+       return false;
     }
 
   if (tree_fits_uhwi_p (len)
@@ -2756,12 +2759,274 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
                                                   boolean_type_node,
                                                   arg1, arg2));
          gimplify_and_update_call_from_tree (gsi, res);
-         return false;
+         return true;
        }
     }
 
   gimple_call_set_fndecl (stmt2, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
-  return false;
+  return true;
+}
+
+/* Given an index to the strinfo vector, compute the string length for the
+   corresponding string. Return -1 when unknown.  */
+static HOST_WIDE_INT 
+compute_string_length (int idx)
+{
+  HOST_WIDE_INT string_leni = -1; 
+  gcc_assert (idx != 0);
+
+  if (idx < 0)
+   return ~idx;
+
+  strinfo *si = get_strinfo (idx);
+  if (si)
+    {
+      tree const_string_len = get_string_length (si);
+      if (const_string_len && tree_fits_shwi_p (const_string_len))
+       string_leni = tree_to_shwi (const_string_len);
+    }
+
+  if (string_leni < 0)
+    return -1;
+
+  return string_leni;
+}
+
+/* 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
+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);
+
+  /* Try to determine the size of the object through the RHS of the 
+     assign statement.  */
+  if (TREE_CODE (dest) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (dest);
+      if (!is_gimple_assign (stmt))
+       return NULL_TREE;
+
+      if (!gimple_assign_single_p (stmt)
+         && !gimple_assign_unary_nop_p (stmt))
+       return NULL_TREE;
+
+      dest = gimple_assign_rhs1 (stmt);
+      return determine_min_objsize (dest);
+    }
+
+  /* Try to determine the size of the object from its type.  */
+  if (TREE_CODE (dest) != ADDR_EXPR)
+    return NULL_TREE;
+
+  tree type = TREE_TYPE (dest);
+  if (TREE_CODE (type) == POINTER_TYPE)
+    type = TREE_TYPE (type);
+
+  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))
+    {
+      tree size_t = TYPE_SIZE_UNIT (type);
+      if (size_t && TREE_CODE (size_t) == INTEGER_CST 
+         && !integer_zerop (size_t))
+        return size_t;
+    }
+
+  return NULL_TREE;
+}
+
+/* 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. 
+ */ 
+
+static bool
+handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
+{
+  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;
+
+  /* When both arguments are unknown, do nothing.  */
+  if (idx1 == 0 && idx2 == 0)
+    return false;
+
+  /* Handle strncmp function.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_shwi_p (len))
+        length = tree_to_shwi (len);
+
+      is_ncmp = 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) 
+    {    
+      gimple *use_stmt = USE_STMT (use_p);
+
+      if (is_gimple_debug (use_stmt))
+        continue;
+      if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
+       {    
+         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 false;
+           }
+         else if (code == EQ_EXPR || code == NE_EXPR)
+           {
+             if (!integer_zerop (gimple_assign_rhs2 (use_stmt)))
+               return false;
+            }
+         else 
+           return false;
+       }
+      else if (gimple_code (use_stmt) == GIMPLE_COND)
+       {
+         tree_code code = gimple_cond_code (use_stmt);
+         if ((code != EQ_EXPR && code != NE_EXPR)
+             || !integer_zerop (gimple_cond_rhs (use_stmt)))
+           return false;
+       }
+      else
+        return false;
+    }
+  
+  /* When the lengths of both arguments are known, and they are unequal, we can 
+     safely fold the call to a non-zero value for strcmp;
+     othewise, do nothing now.  */
+  if (idx1 != 0 && idx2 != 0)
+    {
+      HOST_WIDE_INT const_string_leni1 = compute_string_length (idx1);
+      HOST_WIDE_INT const_string_leni2 = compute_string_length (idx2);
+
+      if (!is_ncmp 
+         && const_string_leni1 != -1
+         && const_string_leni2 != -1
+         && const_string_leni1 != const_string_leni2) 
+       {
+         replace_call_with_value (gsi, integer_one_node); 
+         return true;
+       }
+      return false;
+    }
+
+  /* When the length of one argument is constant.  */
+  tree var_string = NULL_TREE;
+  HOST_WIDE_INT const_string_leni = -1;
+  
+  if (idx1)
+    {
+      const_string_leni = compute_string_length (idx1);
+      var_string = arg2;
+    } 
+  else 
+    {
+      gcc_checking_assert (idx2);
+      const_string_leni = compute_string_length (idx2);
+      var_string = arg1;
+    } 
+
+  if (const_string_leni < 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);
+
+  if (!size)
+    return false;
+  if (tree_fits_uhwi_p (size))
+    var_sizei = tree_to_uhwi (size);
+
+  if (var_sizei == 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;
+
+  /* Replace strcmp or strncmp with the corresponding str(n)cmp_eq.  */
+  if (var_sizei > final_length) 
+    {
+      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);
+    }
+  else 
+    return false;
+
+  return true;
 }
 
 /* Handle a POINTER_PLUS_EXPR statement.
@@ -3209,11 +3474,16 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
            handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
            break;
          case BUILT_IN_MEMSET:
-           if (!handle_builtin_memset (gsi))
+           if (handle_builtin_memset (gsi))
              return false;
            break;
          case BUILT_IN_MEMCMP:
-           if (!handle_builtin_memcmp (gsi))
+           if (handle_builtin_memcmp (gsi))
+             return false;
+           break;
+         case BUILT_IN_STRCMP:
+         case BUILT_IN_STRNCMP:
+           if (handle_builtin_string_cmp (gsi))
              return false;
            break;
          default:
index 8fb13a0cdf823615ab8c9ddc854f53ff62b2e18a..14ab83a9469d6c4378fed54ede3771779bd0c6dd 100644 (file)
@@ -4498,7 +4498,9 @@ find_func_aliases_for_builtin_call (struct function *fn, gcall *t)
          that use the memory pointed to by their arguments (but not
         transitively).  */
       case BUILT_IN_STRCMP:
+      case BUILT_IN_STRCMP_EQ:
       case BUILT_IN_STRNCMP:
+      case BUILT_IN_STRNCMP_EQ:
       case BUILT_IN_STRCASECMP:
       case BUILT_IN_STRNCASECMP:
       case BUILT_IN_MEMCMP:
index e8dc42557d0b13ea3411ae0f9976b11d573fbef6..85ce99373db9044a0843e394de01520ee50ecea3 100644 (file)
@@ -10277,6 +10277,14 @@ build_common_builtin_nodes (void)
                        "__builtin_memcmp_eq",
                        ECF_PURE | ECF_NOTHROW | ECF_LEAF);
 
+  local_define_builtin ("__builtin_strncmp_eq", ftype, BUILT_IN_STRNCMP_EQ,
+                       "__builtin_strncmp_eq",
+                       ECF_PURE | ECF_NOTHROW | ECF_LEAF);
+
+  local_define_builtin ("__builtin_strcmp_eq", ftype, BUILT_IN_STRCMP_EQ,
+                       "__builtin_strcmp_eq",
+                       ECF_PURE | ECF_NOTHROW | ECF_LEAF);
+
   /* If there's a possibility that we might use the ARM EABI, build the
     alternate __cxa_end_cleanup node used to resume from C++.  */
   if (targetm.arm_eabi_unwinder)