From 2dee4af12b1f2a1262a88bdfa40e5a9db5374663 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 9 Nov 2000 10:07:44 +0100 Subject: [PATCH] builtins.c (c_strlen): Use TREE_STRING_LENGTH - 1 for max. * 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 | 19 + gcc/builtins.c | 411 ++++++++++++------ gcc/builtins.def | 1 + gcc/c-common.c | 6 + gcc/testsuite/ChangeLog | 8 + .../gcc.c-torture/execute/string-opt-1.c | 4 +- .../gcc.c-torture/execute/string-opt-3.c | 83 ++++ .../gcc.c-torture/execute/string-opt-4.c | 36 ++ .../gcc.c-torture/execute/string-opt-5.c | 58 +++ 9 files changed, 496 insertions(+), 130 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/execute/string-opt-3.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/string-opt-4.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/string-opt-5.c 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 + + * 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 * 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 + + * 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 * 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; +} -- 2.30.2