summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>2000-11-09 09:07:44 +0000
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>2000-11-09 09:07:44 +0000
commit83d79705157d50abbd704a520e9bd0cf626607b5 (patch)
tree7e4c441f58d15d9c8077a944d506d9f9d2da8ba7
parent47cf73fa40b9ff017869a07789d1527709efc59a (diff)
downloadgcc-83d79705157d50abbd704a520e9bd0cf626607b5.tar.gz
* 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@37338 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog19
-rw-r--r--gcc/builtins.c411
-rw-r--r--gcc/builtins.def1
-rw-r--r--gcc/c-common.c6
-rw-r--r--gcc/testsuite/ChangeLog8
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/string-opt-1.c4
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/string-opt-3.c83
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/string-opt-4.c36
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/string-opt-5.c58
9 files changed, 496 insertions, 130 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5f1bd775139..2f6938dd6e8 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 0021f760757..8430a23f82e 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 261c76a663c..980099c1b96 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -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)
diff --git a/gcc/c-common.c b/gcc/c-common.c
index 247145cf2ab..0482a83eee1 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -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,
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index dff8531f4b1..2757aeac53b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
diff --git a/gcc/testsuite/gcc.c-torture/execute/string-opt-1.c b/gcc/testsuite/gcc.c-torture/execute/string-opt-1.c
index e78f328aad1..3bbc471f850 100644
--- a/gcc/testsuite/gcc.c-torture/execute/string-opt-1.c
+++ b/gcc/testsuite/gcc.c-torture/execute/string-opt-1.c
@@ -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
index 00000000000..b2b9e1fe7d9
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/string-opt-3.c
@@ -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
index 00000000000..a4c70cfda6b
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/string-opt-4.c
@@ -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
index 00000000000..79b451d7f79
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/string-opt-5.c
@@ -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;
+}