[PATCH] Optimize strchr("abc", 'c'), strrchr("abc", 'c'), strcmp("a","b") and strstr("abc","bc") (take 2)
Jakub Jelinek
jakub@redhat.com
Wed Nov 8 04:33:00 GMT 2000
Hi!
Ok, next trial.
This one does nothing for strchr(non-constant-arg, '\0'), but optimizes
strrchr("abc", 'c') and also strrchr(x, '\0') -> strchr(x, '\0').
I've updated strpbrk and fputs builtins to use c_getstr as well (and also
made it const char *).
Bootstrapped on i386-redhat-linux, no regressions.
If this one gets approved, I'll probably try to add strchrM patterns on i386
and use it in the builtin strchr, plus will try to do something more about
strcmp.
Also note that glibc optimizes also strpbrk(x, "ab") and strpbrk(x, "abc")
by inlining a loop which does the checking for each character, I wonder
whether gcc should not do that as well unless -Os. At least glibc strpbrk
sets up a bitmask or char array initially, so the initial strpbrk overhead
is not very small and thus inlining the loop might help.
Maybe some benchmarking on various systems would tell more.
2000-11-07 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.
* 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.
--- gcc/builtins.c.jj Tue Nov 7 22:07:48 2000
+++ gcc/builtins.c Wed Nov 8 12:16:09 2000
@@ -80,6 +80,7 @@ tree (*lang_type_promotes_to) PARAMS ((t
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
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
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,
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,
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;
+ const char *p1;
- 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 (TREE_CODE (s2) != INTEGER_CST)
+ return 0;
- if (result)
- {
- long offset = result - TREE_STRING_POINTER (stripped_s1);
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ const char *r = strchr (p1, (char) TREE_INT_CST_LOW (s2));
- /* 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 (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, tar
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;
+ 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,
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,
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,
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,
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)
--- gcc/c-common.c.jj Tue Nov 7 22:07:48 2000
+++ gcc/c-common.c Wed Nov 8 10:14:40 2000
@@ -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,
@@ -5248,6 +5250,10 @@ c_common_nodes_and_builtins ()
builtin_function ("strcmp", int_ftype_string_string, BUILT_IN_STRCMP,
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);
--- gcc/builtins.def.jj Tue Nov 7 22:07:48 2000
+++ gcc/builtins.def Wed Nov 8 10:16:21 2000
@@ -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)
--- gcc/testsuite/gcc.c-torture/execute/string-opt-1.c.jj Tue Nov 7 23:13:58 2000
+++ gcc/testsuite/gcc.c-torture/execute/string-opt-1.c Wed Nov 8 12:11:19 2000
@@ -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;
}
--- gcc/testsuite/gcc.c-torture/execute/string-opt-3.c.jj Tue Nov 7 12:00:07 2000
+++ gcc/testsuite/gcc.c-torture/execute/string-opt-3.c Wed Nov 8 12:09:16 2000
@@ -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
--- gcc/testsuite/gcc.c-torture/execute/string-opt-4.c.jj Wed Nov 8 11:30:34 2000
+++ gcc/testsuite/gcc.c-torture/execute/string-opt-4.c Wed Nov 8 11:56:54 2000
@@ -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
--- gcc/testsuite/gcc.c-torture/execute/string-opt-5.c.jj Wed Nov 8 11:32:02 2000
+++ gcc/testsuite/gcc.c-torture/execute/string-opt-5.c Wed Nov 8 12:10:41 2000
@@ -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;
+}
Jakub
More information about the Gcc-patches
mailing list