builtins.c (c_strlen): Use TREE_STRING_LENGTH - 1 for max.
authorJakub Jelinek <jakub@redhat.com>
Thu, 9 Nov 2000 09:07:44 +0000 (10:07 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 9 Nov 2000 09:07:44 +0000 (10:07 +0100)
* builtins.c (c_strlen): Use TREE_STRING_LENGTH - 1 for max.
(c_getstr): New function.
(expand_builtin_strstr): Do nothing if -fcheck-memory-usage.
If both arguments are constant string, optimize out.
(expand_builtin_strchr, expand_builtin_strrchr): New functions.
(expand_builtin_strpbrk): Use c_getstr, do nothing if
-fcheck-memory-usage.
(expand_builtin_fputs): Likewise.
(expand_builtin_strcmp): Add MODE argument.
Use even if !HAVE_cmpstrsi.
Optimize the case when both arguments are constant strings.
(expand_builtin): Adjust expand_builtin_strcmp caller.
Call expand_builtin_strchr and expand_builtin_strrchr.
* c-common.c (c_common_nodes_and_builtins): Add strchr and strrchr
builtins.
* builtins.def (BUILT_IN_STRRCHR): Add.

* gcc.c-torture/execute/string-opt-1.c: Add test for strstr
with both arguments constant strings.
* gcc.c-torture/execute/string-opt-3.c: New test.
* gcc.c-torture/execute/string-opt-4.c: New test.
* gcc.c-torture/execute/string-opt-5.c: New test.

From-SVN: r37338

gcc/ChangeLog
gcc/builtins.c
gcc/builtins.def
gcc/c-common.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/string-opt-1.c
gcc/testsuite/gcc.c-torture/execute/string-opt-3.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/string-opt-4.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/string-opt-5.c [new file with mode: 0644]

index 5f1bd77..2f6938d 100644 (file)
@@ -1,3 +1,22 @@
+2000-11-09  Jakub Jelinek  <jakub@redhat.com>
+
+       * builtins.c (c_strlen): Use TREE_STRING_LENGTH - 1 for max.
+       (c_getstr): New function.
+       (expand_builtin_strstr): Do nothing if -fcheck-memory-usage.
+       If both arguments are constant string, optimize out.
+       (expand_builtin_strchr, expand_builtin_strrchr): New functions.
+       (expand_builtin_strpbrk): Use c_getstr, do nothing if
+       -fcheck-memory-usage.
+       (expand_builtin_fputs): Likewise.
+       (expand_builtin_strcmp): Add MODE argument.
+       Use even if !HAVE_cmpstrsi.
+       Optimize the case when both arguments are constant strings.
+       (expand_builtin): Adjust expand_builtin_strcmp caller.
+       Call expand_builtin_strchr and expand_builtin_strrchr.
+       * c-common.c (c_common_nodes_and_builtins): Add strchr and strrchr
+       builtins.
+       * builtins.def (BUILT_IN_STRRCHR): Add.
+
 2000-11-08  Gerald Pfeifer  <pfeifer@dbai.tuwien.ac.at>
 
        * fixinc/gnu-regex.c: Rename EGCS LOCAL markers to GCC LOCAL.
index 0021f76..8430a23 100644 (file)
@@ -80,6 +80,7 @@ tree (*lang_type_promotes_to) PARAMS ((tree));
 
 static int get_pointer_alignment       PARAMS ((tree, unsigned));
 static tree c_strlen                   PARAMS ((tree));
+static const char *c_getstr            PARAMS ((tree));
 static rtx get_memory_rtx              PARAMS ((tree));
 static int apply_args_size             PARAMS ((void));
 static int apply_result_size           PARAMS ((void));
@@ -100,8 +101,9 @@ static rtx expand_builtin_va_end    PARAMS ((tree));
 static rtx expand_builtin_va_copy      PARAMS ((tree));
 #ifdef HAVE_cmpstrsi
 static rtx expand_builtin_memcmp       PARAMS ((tree, tree, rtx));
-static rtx expand_builtin_strcmp       PARAMS ((tree, rtx));
 #endif
+static rtx expand_builtin_strcmp       PARAMS ((tree, rtx,
+                                                enum machine_mode));
 static rtx expand_builtin_memcpy       PARAMS ((tree));
 static rtx expand_builtin_strcpy       PARAMS ((tree));
 static rtx expand_builtin_memset       PARAMS ((tree));
@@ -111,6 +113,10 @@ static rtx expand_builtin_strstr   PARAMS ((tree, rtx,
                                                 enum machine_mode));
 static rtx expand_builtin_strpbrk      PARAMS ((tree, rtx,
                                                 enum machine_mode));
+static rtx expand_builtin_strchr       PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx expand_builtin_strrchr      PARAMS ((tree, rtx,
+                                                enum machine_mode));
 static rtx expand_builtin_alloca       PARAMS ((tree, rtx));
 static rtx expand_builtin_ffs          PARAMS ((tree, rtx, rtx));
 static rtx expand_builtin_frame_address        PARAMS ((tree));
@@ -210,7 +216,7 @@ c_strlen (src)
   if (src == 0)
     return 0;
 
-  max = TREE_STRING_LENGTH (src);
+  max = TREE_STRING_LENGTH (src) - 1;
   ptr = TREE_STRING_POINTER (src);
 
   if (offset_node && TREE_CODE (offset_node) != INTEGER_CST)
@@ -263,6 +269,41 @@ c_strlen (src)
   return ssize_int (strlen (ptr + offset));
 }
 
+/* Return a char pointer for a C string if it is a string constant
+   or sum of string constant and integer constant.  */
+
+static const char *
+c_getstr (src)
+     tree src;
+{
+  tree offset_node;
+  int offset, max;
+  char *ptr;
+
+  src = string_constant (src, &offset_node);
+  if (src == 0)
+    return 0;
+
+  max = TREE_STRING_LENGTH (src) - 1;
+  ptr = TREE_STRING_POINTER (src);
+
+  if (!offset_node)
+    offset = 0;
+  else if (TREE_CODE (offset_node) != INTEGER_CST)
+    return 0;
+  else
+    {
+      /* Did we get a long long offset?  If so, punt.  */
+      if (TREE_INT_CST_HIGH (offset_node) != 0)
+       return 0;
+      offset = TREE_INT_CST_LOW (offset_node);
+      if (offset < 0 || offset > max)
+       return 0;
+    }
+
+  return (const char *) ptr + offset;
+}
+
 /* Given TEM, a pointer to a stack frame, follow the dynamic chain COUNT
    times to get the address of either a higher stack frame, or a return
    address located within it (depending on FNDECL_CODE).  */
@@ -1416,57 +1457,63 @@ expand_builtin_strstr (arglist, target, mode)
   if (arglist == 0
       || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
       || TREE_CHAIN (arglist) == 0
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
+      || current_function_check_memory_usage)
     return 0;
   else
     {
       tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
-      tree len = c_strlen (s2);
+      tree call_expr, fn;
+      const char *p1, *p2;
 
-      if (!len)
+      p2 = c_getstr (s2);
+      if (p2 == NULL)
        return 0;
 
-      switch (compare_tree_int (len, 1))
-        {
-       case -1: /* length is 0, return s1.  */
-         return expand_expr (s1, target, mode, EXPAND_NORMAL);
-       case 0: /* length is 1, return strchr(s1, s2[0]).  */
-         {
-           tree call_expr, fn = built_in_decls[BUILT_IN_STRCHR];
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         const char *r = strstr (p1, p2);
 
-           if (!fn)
-             return 0;
-           STRIP_NOPS (s2);
-           if (s2 && TREE_CODE (s2) == ADDR_EXPR)
-             s2 = TREE_OPERAND (s2, 0);
+         if (r == NULL)
+           return const0_rtx;
 
-           /* New argument list transforming strstr(s1, s2) to
-              strchr(s1, s2[0]).  */
-           arglist =
-             build_tree_list (NULL_TREE,
-                              build_int_2 (TREE_STRING_POINTER (s2)[0], 0));
-           arglist = tree_cons (NULL_TREE, s1, arglist);
-           call_expr = build1 (ADDR_EXPR,
-                               build_pointer_type (TREE_TYPE (fn)), fn);
-           call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
-                              call_expr, arglist, NULL_TREE);
-           TREE_SIDE_EFFECTS (call_expr) = 1;
-           return expand_expr (call_expr, target, mode, EXPAND_NORMAL);
-         }
-       case 1: /* length is greater than 1, really call strstr.  */
-         return 0;
-       default:
-         abort();
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
        }
+
+      if (p2[0] == '\0')
+       return expand_expr (s1, target, mode, EXPAND_NORMAL);
+
+      if (p2[1] != '\0')
+       return 0;
+
+      fn = built_in_decls[BUILT_IN_STRCHR];
+      if (!fn)
+       return 0;
+
+      /* New argument list transforming strstr(s1, s2) to
+        strchr(s1, s2[0]).  */
+      arglist =
+       build_tree_list (NULL_TREE, build_int_2 (p2[0], 0));
+      arglist = tree_cons (NULL_TREE, s1, arglist);
+      call_expr = build1 (ADDR_EXPR,
+                         build_pointer_type (TREE_TYPE (fn)), fn);
+      call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+                        call_expr, arglist, NULL_TREE);
+      TREE_SIDE_EFFECTS (call_expr) = 1;
+      return expand_expr (call_expr, target, mode, EXPAND_NORMAL);
     }
 }
 
-/* Expand a call to the strpbrk builtin.  Return 0 if we failed the
+/* Expand a call to the strchr builtin.  Return 0 if we failed the
    caller should emit a normal call, otherwise try to get the result
    in TARGET, if convenient (and in mode MODE if that's convenient).  */
 
 static rtx
-expand_builtin_strpbrk (arglist, target, mode)
+expand_builtin_strchr (arglist, target, mode)
      tree arglist;
      rtx target;
      enum machine_mode mode;
@@ -1474,84 +1521,160 @@ expand_builtin_strpbrk (arglist, target, mode)
   if (arglist == 0
       || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
       || TREE_CHAIN (arglist) == 0
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != INTEGER_TYPE
+      || current_function_check_memory_usage)
     return 0;
   else
     {
       tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
-      tree len1 = c_strlen (s1), len2 = c_strlen (s2);
-      tree stripped_s1 = s1, stripped_s2 = s2;
-
-      STRIP_NOPS (stripped_s1);
-      if (stripped_s1 && TREE_CODE (stripped_s1) == ADDR_EXPR)
-       stripped_s1 = TREE_OPERAND (stripped_s1, 0);
-      STRIP_NOPS (stripped_s2);
-      if (stripped_s2 && TREE_CODE (stripped_s2) == ADDR_EXPR)
-       stripped_s2 = TREE_OPERAND (stripped_s2, 0);
-
-      /* If both arguments are constants, calculate the result now.  */
-      if (len1 && len2
-         && TREE_CODE (stripped_s1) == STRING_CST
-         && TREE_CODE (stripped_s2) == STRING_CST)
-        {
-         const char *const result =
-           strpbrk (TREE_STRING_POINTER (stripped_s1),
-                    TREE_STRING_POINTER (stripped_s2));
-
-         if (result)
-           {
-             long offset = result - TREE_STRING_POINTER (stripped_s1);
+      const char *p1;
 
-             /* Return an offset into the constant string argument.  */
-             return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
-                                              s1, ssize_int (offset))),
-                                 target, mode, EXPAND_NORMAL);
-           }
-         else
+      if (TREE_CODE (s2) != INTEGER_CST)
+       return 0;
+
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         const char *r = strchr (p1, (char) TREE_INT_CST_LOW (s2));
+
+         if (r == NULL)
            return const0_rtx;
+
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
        }
 
-      /* We must have been able to figure out the second argument's
-         length to do anything else.  */
-      if (!len2)
+      /* FIXME: Should use here strchrM optab so that ports can optimize
+        this.  */
+      return 0;
+    }
+}
+
+/* Expand a call to the strrchr builtin.  Return 0 if we failed the
+   caller should emit a normal call, otherwise try to get the result
+   in TARGET, if convenient (and in mode MODE if that's convenient).  */
+
+static rtx
+expand_builtin_strrchr (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  if (arglist == 0
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+      || TREE_CHAIN (arglist) == 0
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != INTEGER_TYPE
+      || current_function_check_memory_usage)
+    return 0;
+  else
+    {
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      tree call_expr, fn;
+      const char *p1;
+
+      if (TREE_CODE (s2) != INTEGER_CST)
        return 0;
 
-      /* OK, handle some cases.  */
-      switch (compare_tree_int (len2, 1))
-        {
-       case -1: /* length is 0, return NULL.  */
-         {
-           /* Evaluate and ignore the arguments in case they had
-              side-effects.  */
-           expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL);
-           expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         const char *r = strrchr (p1, (char) TREE_INT_CST_LOW (s2));
+
+         if (r == NULL)
            return const0_rtx;
-         }
-       case 0: /* length is 1, return strchr(s1, s2[0]).  */
-         {
-           tree call_expr, fn = built_in_decls[BUILT_IN_STRCHR];
 
-           if (!fn)
-             return 0;
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
+       }
 
-           /* New argument list transforming strpbrk(s1, s2) to
-              strchr(s1, s2[0]).  */
-           arglist =
-             build_tree_list (NULL_TREE, build_int_2
-                              (TREE_STRING_POINTER (stripped_s2)[0], 0));
-           arglist = tree_cons (NULL_TREE, s1, arglist);
-           call_expr = build1 (ADDR_EXPR,
-                               build_pointer_type (TREE_TYPE (fn)), fn);
-           call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
-                              call_expr, arglist, NULL_TREE);
-           TREE_SIDE_EFFECTS (call_expr) = 1;
-           return expand_expr (call_expr, target, mode, EXPAND_NORMAL);
-         }
-       case 1: /* length is greater than 1, really call strpbrk.  */
-         return 0;
-       default:
-         abort();
+      if (! integer_zerop (s2))
+       return 0;
+
+      fn = built_in_decls[BUILT_IN_STRCHR];
+      if (!fn)
+       return 0;
+
+      /* Transform strrchr(s1, '\0') to strchr(s1, '\0').  */
+      call_expr = build1 (ADDR_EXPR,
+                         build_pointer_type (TREE_TYPE (fn)), fn);
+      call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+                        call_expr, arglist, NULL_TREE);
+      TREE_SIDE_EFFECTS (call_expr) = 1;
+      return expand_expr (call_expr, target, mode, EXPAND_NORMAL);
+    }
+}
+
+/* Expand a call to the strpbrk builtin.  Return 0 if we failed the
+   caller should emit a normal call, otherwise try to get the result
+   in TARGET, if convenient (and in mode MODE if that's convenient).  */
+
+static rtx
+expand_builtin_strpbrk (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  if (arglist == 0
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+      || TREE_CHAIN (arglist) == 0
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
+      || current_function_check_memory_usage)
+    return 0;
+  else
+    {
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      tree call_expr, fn;
+      const char *p1, *p2;
+
+      p2 = c_getstr (s2);
+      if (p2 == NULL)
+       return 0;
+
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         const char *r = strpbrk (p1, p2);
+
+         if (r == NULL)
+           return const0_rtx;
+
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
        }
+
+      if (p2[0] == '\0')
+       {
+         /* strpbrk(x, "") == NULL.
+            Evaluate and ignore the arguments in case they had
+            side-effects.  */
+         expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return const0_rtx;
+       }
+
+      if (p2[1] != '\0')
+       return 0;  /* Really call strpbrk.  */
+
+      fn = built_in_decls[BUILT_IN_STRCHR];
+      if (!fn)
+       return 0;
+
+      /* New argument list transforming strpbrk(s1, s2) to
+        strchr(s1, s2[0]).  */
+      arglist =
+       build_tree_list (NULL_TREE, build_int_2 (p2[0], 0));
+      arglist = tree_cons (NULL_TREE, s1, arglist);
+      call_expr = build1 (ADDR_EXPR,
+                         build_pointer_type (TREE_TYPE (fn)), fn);
+      call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+                        call_expr, arglist, NULL_TREE);
+      TREE_SIDE_EFFECTS (call_expr) = 1;
+      return expand_expr (call_expr, target, mode, EXPAND_NORMAL);
     }
 }
 
@@ -1832,17 +1955,21 @@ expand_builtin_memcmp (exp, arglist, target)
       return convert_to_mode (mode, result, 0);
   }
 }
+#endif
 
 /* Expand expression EXP, which is a call to the strcmp builtin.  Return 0
    if we failed the caller should emit a normal call, otherwise try to get
    the result in TARGET, if convenient.  */
 
 static rtx
-expand_builtin_strcmp (exp, target)
+expand_builtin_strcmp (exp, target, mode)
      tree exp;
      rtx target;
+     enum machine_mode mode;
 {
   tree arglist = TREE_OPERAND (exp, 1);
+  tree arg1, arg2;
+  const char *p1, *p2;
 
   /* If we need to check memory accesses, call the library function.  */
   if (current_function_check_memory_usage)
@@ -1856,11 +1983,27 @@ expand_builtin_strcmp (exp, target)
          != POINTER_TYPE))
     return 0;
 
-  else if (! HAVE_cmpstrsi)
+  arg1 = TREE_VALUE (arglist);
+  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+
+  p1 = c_getstr (arg1);
+  p2 = c_getstr (arg2);
+
+  if (p1 && p2)
+    {
+      int i = strcmp (p1, p2);
+
+      return expand_expr (i < 0 ? build_int_2 (-1, -1)
+                               : i == 0 ? integer_zero_node
+                                        : integer_one_node,
+                         target, mode, EXPAND_NORMAL);
+    }
+
+#ifdef HAVE_cmpstrsi
+  if (! HAVE_cmpstrsi)
     return 0;
+
   {
-    tree arg1 = TREE_VALUE (arglist);
-    tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
     tree len = c_strlen (arg1);
     tree len2 = c_strlen (arg2);
     rtx result;
@@ -1900,8 +2043,10 @@ expand_builtin_strcmp (exp, target)
 
     return result;
   }
-}
+#else
+  return 0;
 #endif
+}
 
 /* Expand a call to __builtin_saveregs, generating the result in TARGET,
    if that's convenient.  */
@@ -2464,7 +2609,8 @@ expand_builtin_fputs (arglist, ignore)
       || (TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
       || TREE_CHAIN (arglist) == 0
       || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
-         != POINTER_TYPE))
+         != POINTER_TYPE)
+      || current_function_check_memory_usage)
     return 0;
 
   /* Get the length of the string passed to fputs.  If the length
@@ -2484,23 +2630,21 @@ expand_builtin_fputs (arglist, ignore)
       }
     case 0: /* length is 1, call fputc.  */
       {
-       tree stripped_string = TREE_VALUE (arglist);
+       const char *p = c_getstr (TREE_VALUE (arglist));
 
-       STRIP_NOPS (stripped_string);
-       if (stripped_string && TREE_CODE (stripped_string) == ADDR_EXPR)
-         stripped_string = TREE_OPERAND (stripped_string, 0);
-      
-       /* New argument list transforming fputs(string, stream) to
-          fputc(string[0], stream).  */
-       arglist =
-         build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
-       arglist =
-         tree_cons (NULL_TREE, 
-                    build_int_2 (TREE_STRING_POINTER (stripped_string)[0], 0),
-                    arglist);
-       fn = fn_fputc;
-       break;
+       if (p != NULL)
+         {      
+           /* New argument list transforming fputs(string, stream) to
+              fputc(string[0], stream).  */
+           arglist =
+             build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
+           arglist =
+             tree_cons (NULL_TREE, build_int_2 (p[0], 0), arglist);
+           fn = fn_fputc;
+           break;
+         }
       }
+      /* FALLTHROUGH */
     case 1: /* length is greater than 1, call fwrite.  */
       {
        tree string_arg = TREE_VALUE (arglist);
@@ -2740,6 +2884,18 @@ expand_builtin (exp, target, subtarget, mode, ignore)
        return target;
       break;
       
+    case BUILT_IN_STRCHR:
+      target = expand_builtin_strchr (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_STRRCHR:
+      target = expand_builtin_strrchr (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_MEMCPY:
       target = expand_builtin_memcpy (arglist);
       if (target)
@@ -2758,16 +2914,16 @@ expand_builtin (exp, target, subtarget, mode, ignore)
        return target;
       break;
 
-/* These comparison functions need an instruction that returns an actual
-   index.  An ordinary compare that just sets the condition codes
-   is not enough.  */
-#ifdef HAVE_cmpstrsi
     case BUILT_IN_STRCMP:
-      target = expand_builtin_strcmp (exp, target);
+      target = expand_builtin_strcmp (exp, target, mode);
       if (target)
        return target;
       break;
 
+/* These comparison functions need an instruction that returns an actual
+   index.  An ordinary compare that just sets the condition codes
+   is not enough.  */
+#ifdef HAVE_cmpstrsi
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       target = expand_builtin_memcmp (exp, arglist, target);
@@ -2775,7 +2931,6 @@ expand_builtin (exp, target, subtarget, mode, ignore)
        return target;
       break;
 #else
-    case BUILT_IN_STRCMP:
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       break;
@@ -2833,9 +2988,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
     case BUILT_IN_PUTS:
     case BUILT_IN_FPUTC:
     case BUILT_IN_FWRITE:
-    case BUILT_IN_STRCHR:
       break;
-      
     case BUILT_IN_FPUTS:
       target = expand_builtin_fputs (arglist, ignore);
       if (target)
index 261c76a..980099c 100644 (file)
@@ -42,6 +42,7 @@ DEF_BUILTIN(BUILT_IN_STRLEN)
 DEF_BUILTIN(BUILT_IN_STRSTR)
 DEF_BUILTIN(BUILT_IN_STRPBRK)
 DEF_BUILTIN(BUILT_IN_STRCHR)
+DEF_BUILTIN(BUILT_IN_STRRCHR)
 DEF_BUILTIN(BUILT_IN_FSQRT)
 DEF_BUILTIN(BUILT_IN_SIN)
 DEF_BUILTIN(BUILT_IN_COS)
index 247145c..0482a83 100644 (file)
@@ -5179,6 +5179,8 @@ c_common_nodes_and_builtins ()
   built_in_decls[BUILT_IN_STRCHR] =
     builtin_function ("__builtin_strchr", string_ftype_string_int,
                    BUILT_IN_STRCHR, BUILT_IN_NORMAL, "strchr");
+  builtin_function ("__builtin_strrchr", string_ftype_string_int,
+                   BUILT_IN_STRRCHR, BUILT_IN_NORMAL, "strrchr");
   builtin_function ("__builtin_strcpy", string_ftype_ptr_ptr,
                    BUILT_IN_STRCPY, BUILT_IN_NORMAL, "strcpy");
   builtin_function ("__builtin_strlen", strlen_ftype,
@@ -5249,6 +5251,10 @@ c_common_nodes_and_builtins ()
                        BUILT_IN_NORMAL, NULL_PTR);
       builtin_function ("strstr", string_ftype_string_string, BUILT_IN_STRSTR,
                        BUILT_IN_NORMAL, NULL_PTR);
+      builtin_function ("strchr", string_ftype_string_int, BUILT_IN_STRCHR,
+                       BUILT_IN_NORMAL, NULL_PTR);
+      builtin_function ("strrchr", string_ftype_string_int, BUILT_IN_STRRCHR,
+                       BUILT_IN_NORMAL, NULL_PTR);
       builtin_function ("strpbrk", string_ftype_string_string, BUILT_IN_STRPBRK,
                        BUILT_IN_NORMAL, NULL_PTR);
       builtin_function ("strcpy", string_ftype_ptr_ptr, BUILT_IN_STRCPY,
index dff8531..2757aea 100644 (file)
@@ -1,3 +1,11 @@
+2000-11-09  Jakub Jelinek  <jakub@redhat.com>
+
+       * gcc.c-torture/execute/string-opt-1.c: Add test for strstr
+       with both arguments constant strings.
+       * gcc.c-torture/execute/string-opt-3.c: New test.
+       * gcc.c-torture/execute/string-opt-4.c: New test.
+       * gcc.c-torture/execute/string-opt-5.c: New test.
+
 2000-11-08  Nick Clifton  <nickc@redhat.com>
 
        * gcc.c-torture/execute/20001108-1.c: New test case.  Checks
index e78f328..3bbc471 100644 (file)
@@ -22,7 +22,9 @@ int main()
     abort();
   if (strstr (foo + 6, "o") != foo + 7)
     abort();
-  
+  if (strstr (foo + 1, "world") != foo + 6)
+    abort();
+
   return 0;
 }
 
diff --git a/gcc/testsuite/gcc.c-torture/execute/string-opt-3.c b/gcc/testsuite/gcc.c-torture/execute/string-opt-3.c
new file mode 100644 (file)
index 0000000..b2b9e1f
--- /dev/null
@@ -0,0 +1,83 @@
+/* Copyright (C) 2000  Free Software Foundation.
+
+   Ensure all expected transformations of builtin strlen, strcmp and strrchr
+   occur and perform correctly.
+
+   Written by Jakub Jelinek, 11/7/2000.  */
+
+extern void abort (void);
+extern __SIZE_TYPE__ strlen (const char *);
+extern int strcmp (const char *, const char *);
+extern char *strrchr (const char *, int);
+
+int x = 6;
+char *bar = "hi world";
+
+int main()
+{
+  const char *const foo = "hello world";
+
+  if (strlen (foo) != 11)
+    abort ();
+  if (strlen (foo + 4) != 7)
+    abort ();
+  if (strlen (foo + (x++ & 7)) != 5)
+    abort ();
+  if (x != 7)
+    abort ();
+  if (strcmp (foo, "hello") <= 0)
+    abort ();
+  if (strcmp (foo + 2, "llo") <= 0)
+    abort ();
+  if (strcmp (foo, foo) != 0)
+    abort ();
+  if (strcmp (foo, "hello world ") >= 0)
+    abort ();
+  if (strcmp (foo + 10, "dx") >= 0)
+    abort ();
+  if (strcmp (10 + foo, "dx") >= 0)
+    abort ();
+  if (strrchr (foo, 'x'))
+    abort ();
+  if (strrchr (foo, 'o') != foo + 7)
+    abort ();
+  if (strrchr (foo, 'e') != foo + 1)
+    abort ();
+  if (strrchr (foo + 3, 'e'))
+    abort ();
+  if (strrchr (foo, '\0') != foo + 11)
+    abort ();
+  if (strrchr (bar, '\0') != bar + 8)
+    abort ();
+  if (strrchr (bar + 4, '\0') != bar + 8)
+    abort ();
+  if (strrchr (bar + (x++ & 3), '\0') != bar + 8)
+    abort ();
+  if (x != 8)
+    abort ();
+
+  return 0;
+}
+
+#ifdef __OPTIMIZE__
+/* When optimizing, all the above cases should be transformed into
+   something else.  So any remaining calls to the original function
+   should abort.  */
+__SIZE_TYPE__
+strlen (const char *s)
+{
+  abort ();
+}
+
+int
+strcmp (const char *s1, const char *s2)
+{
+  abort ();
+}
+
+char *
+strrchr (const char *s, int c)
+{
+  abort ();
+}
+#endif
diff --git a/gcc/testsuite/gcc.c-torture/execute/string-opt-4.c b/gcc/testsuite/gcc.c-torture/execute/string-opt-4.c
new file mode 100644 (file)
index 0000000..a4c70cf
--- /dev/null
@@ -0,0 +1,36 @@
+/* Copyright (C) 2000  Free Software Foundation.
+
+   Ensure all expected transformations of builtin strchr occur and
+   perform correctly.
+
+   Written by Jakub Jelinek, 11/7/2000.  */
+
+extern void abort (void);
+extern char *strchr (const char *, int);
+
+int main()
+{
+  const char *const foo = "hello world";
+
+  if (strchr (foo, 'x'))
+    abort ();
+  if (strchr (foo, 'o') != foo + 4)
+    abort ();
+  if (strchr (foo + 5, 'o') != foo + 7)
+    abort ();
+  if (strchr (foo, '\0')  != foo + 11)
+    abort ();
+
+  return 0;
+}
+
+#ifdef __OPTIMIZE__
+/* When optimizing, all the above cases should be transformed into
+   something else.  So any remaining calls to the original function
+   should abort.  */
+char *
+strchr (const char *s, int c)
+{
+  abort ();
+}
+#endif
diff --git a/gcc/testsuite/gcc.c-torture/execute/string-opt-5.c b/gcc/testsuite/gcc.c-torture/execute/string-opt-5.c
new file mode 100644 (file)
index 0000000..79b451d
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright (C) 2000  Free Software Foundation.
+
+   Ensure builtin strlen, strcmp, strchr and strrchr perform correctly.
+
+   Written by Jakub Jelinek, 11/7/2000.  */
+
+extern void abort (void);
+extern __SIZE_TYPE__ strlen (const char *);
+extern int strcmp (const char *, const char *);
+extern char *strchr (const char *, int);
+extern char *strrchr (const char *, int);
+
+int x = 6;
+char *bar = "hi world";
+
+int main()
+{
+  const char *const foo = "hello world";
+
+  if (strlen (bar) != 8)
+    abort ();
+  if (strlen (bar + (++x & 2)) != 6)
+    abort ();
+  if (x != 7)
+    abort ();
+  if (strlen (foo + (x++, 6)) != 5)
+    abort ();
+  if (x != 8)
+    abort ();
+  if (strlen (foo + (++x & 1)) != 10)
+    abort ();
+  if (x != 9)
+    abort ();
+  if (strcmp (foo + (x -= 6), "lo world"))
+    abort ();
+  if (x != 3)
+    abort ();
+  if (strcmp (foo, bar) >= 0)
+    abort ();
+  if (strcmp (foo, bar + (x++ & 1)) >= 0)
+    abort ();
+  if (x != 4)
+    abort ();
+  if (strchr (foo + (x++ & 7), 'l') != foo + 9)
+    abort ();
+  if (x != 5)
+    abort ();
+  if (strchr (bar, 'o') != bar + 4)
+    abort ();
+  if (strchr (bar, '\0')  != bar + 8)
+    abort ();
+  if (strrchr (bar, 'x'))
+    abort ();
+  if (strrchr (bar, 'o') != bar + 4)
+    abort ();
+
+  return 0;
+}