From da9e9f0862f6ba1c843d2241329a368b509a450b Mon Sep 17 00:00:00 2001 From: "Kaveh R. Ghazi" Date: Mon, 27 Nov 2000 05:00:06 +0000 Subject: [PATCH] builtins.c (expand_builtin_strncmp, [...]): New functions. * builtins.c (expand_builtin_strncmp, expand_builtin_strncpy): New functions. (expand_builtin): Handle BUILT_IN_STRNCPY and BUILT_IN_STRNCMP. * builtins.def (BUILT_IN_STRNCPY, BUILT_IN_STRNCMP): New entries. * c-common.c (c_common_nodes_and_builtins): Declare builtin strncpy and strncmp. * extend.texi (strncmp, strncpy): Document new builtins. testsuite: * gcc.c-torture/execute/string-opt-7.c: New test. * gcc.c-torture/execute/string-opt-8.c: Likewise. From-SVN: r37777 --- gcc/ChangeLog | 13 ++ gcc/builtins.c | 151 ++++++++++++++++++ gcc/builtins.def | 2 + gcc/c-common.c | 30 +++- gcc/extend.texi | 11 +- .../gcc.c-torture/execute/string-opt-7.c | 71 ++++++++ .../gcc.c-torture/execute/string-opt-8.c | 60 +++++++ 7 files changed, 332 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/execute/string-opt-7.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/string-opt-8.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b6e8264f7cf..44ad079ae68 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2000-11-26 Kaveh R. Ghazi + + * builtins.c (expand_builtin_strncmp, expand_builtin_strncpy): New + functions. + (expand_builtin): Handle BUILT_IN_STRNCPY and BUILT_IN_STRNCMP. + + * builtins.def (BUILT_IN_STRNCPY, BUILT_IN_STRNCMP): New entries. + + * c-common.c (c_common_nodes_and_builtins): Declare builtin + strncpy and strncmp. + + * extend.texi (strncmp, strncpy): Document new builtins. + 2000-11-26 Mark Mitchell * config/elfos.h (ASM_OUTPUT_SECTION_NAME): Use a hash table, not diff --git a/gcc/builtins.c b/gcc/builtins.c index 221cba6f34b..7db9b2622a2 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -104,8 +104,12 @@ static rtx expand_builtin_memcmp PARAMS ((tree, tree, rtx)); #endif static rtx expand_builtin_strcmp PARAMS ((tree, rtx, enum machine_mode)); +static rtx expand_builtin_strncmp PARAMS ((tree, rtx, + enum machine_mode)); static rtx expand_builtin_memcpy PARAMS ((tree)); static rtx expand_builtin_strcpy PARAMS ((tree)); +static rtx expand_builtin_strncpy PARAMS ((tree, rtx, + enum machine_mode)); static rtx expand_builtin_memset PARAMS ((tree)); static rtx expand_builtin_bzero PARAMS ((tree)); static rtx expand_builtin_strlen PARAMS ((tree, rtx)); @@ -1770,6 +1774,63 @@ expand_builtin_strcpy (exp) return result; } +/* Expand expression EXP, which is a call to the strncpy builtin. Return 0 + if we failed the caller should emit a normal call. */ + +static rtx +expand_builtin_strncpy (arglist, target, mode) + tree arglist; + rtx target; + enum machine_mode mode; +{ + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || 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_CHAIN (TREE_CHAIN (arglist)) == 0 + || (TREE_CODE (TREE_TYPE (TREE_VALUE + (TREE_CHAIN (TREE_CHAIN (arglist))))) + != INTEGER_TYPE)) + return 0; + else + { + tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist))); + tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* We must be passed a constant len parameter. */ + if (TREE_CODE (len) != INTEGER_CST) + return 0; + + /* If the len parameter is zero, return the dst parameter. */ + if (compare_tree_int (len, 0) == 0) + { + /* Evaluate and ignore the src argument in case it has + side-effects. */ + expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx, + VOIDmode, EXPAND_NORMAL); + /* Return the dst parameter. */ + return expand_expr (TREE_VALUE (arglist), target, mode, + EXPAND_NORMAL); + } + + /* Now, we must be passed a constant src ptr parameter. */ + if (slen == 0) + return 0; + + slen = size_binop (PLUS_EXPR, slen, ssize_int (1)); + + /* We're required to pad with trailing zeros if the requested + len is greater than strlen(s2)+1, so in that case punt. */ + if (tree_int_cst_lt (slen, len)) + return 0; + + /* OK transform into builtin memcpy. */ + return expand_builtin_memcpy (arglist); + } +} + /* Expand expression EXP, which is a call to the memset builtin. Return 0 if we failed the caller should emit a normal call. */ @@ -2051,6 +2112,83 @@ expand_builtin_strcmp (exp, target, mode) #endif } +/* Expand expression EXP, which is a call to the strncmp 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_strncmp (exp, target, mode) + tree exp; + rtx target; + enum machine_mode mode; +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg1, arg2, arg3; + const char *p1, *p2; + + /* If we need to check memory accesses, call the library function. */ + if (current_function_check_memory_usage) + return 0; + + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || 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_CHAIN (TREE_CHAIN (arglist)) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE) + return 0; + + arg1 = TREE_VALUE (arglist); + arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* We must be passed a constant len parameter. */ + if (TREE_CODE (arg3) != INTEGER_CST) + return 0; + + /* If the len parameter is zero, return zero. */ + if (compare_tree_int (arg3, 0) == 0) + { + /* Evaluate and ignore arg1 and arg2 in case they have + side-effects. */ + expand_expr (arg1, const0_rtx, VOIDmode, EXPAND_NORMAL); + expand_expr (arg2, const0_rtx, VOIDmode, EXPAND_NORMAL); + return const0_rtx; + } + + p1 = c_getstr (arg1); + p2 = c_getstr (arg2); + + /* If all arguments are constant, evaluate at compile-time. */ + if (p1 && p2) + { + const int r = strncmp (p1, p2, TREE_INT_CST_LOW (arg3)); + return (r < 0 ? constm1_rtx : (r > 0 ? const1_rtx : const0_rtx)); + } + + /* If either string parameter is constant and its strlen is strictly + less than the length parameter, call expand_builtin_strcmp(). */ + if ((p1 && compare_tree_int (arg3, strlen (p1)) > 0) + || (p2 && compare_tree_int (arg3, strlen (p2)) > 0)) + { + tree newarglist = + tree_cons (NULL_TREE, arg1, build_tree_list (NULL_TREE, arg2)); + rtx result; + + /* Call expand_builtin_strcmp with the modified newarglist. If + the expansion does not occur, do not allow strncmp to expand to + strcmp since strcmp requires that both strings be NULL + terminated whereas strncmp does not. */ + TREE_OPERAND (exp, 1) = newarglist; + result = expand_builtin_strcmp (exp, target, mode); + /* Always restore the original arguments. */ + TREE_OPERAND (exp, 1) = arglist; + return result; + } + + return 0; +} + /* Expand a call to __builtin_saveregs, generating the result in TARGET, if that's convenient. */ @@ -2752,6 +2890,7 @@ expand_builtin (exp, target, subtarget, mode, ignore) || fcode == BUILT_IN_INDEX || fcode == BUILT_IN_RINDEX || fcode == BUILT_IN_STRCHR || fcode == BUILT_IN_STRRCHR || fcode == BUILT_IN_STRLEN || fcode == BUILT_IN_STRCPY + || fcode == BUILT_IN_STRNCPY || fcode == BUILT_IN_STRNCMP || fcode == BUILT_IN_STRSTR || fcode == BUILT_IN_STRPBRK || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS || fcode == BUILT_IN_PUTCHAR || fcode == BUILT_IN_PUTS @@ -2881,6 +3020,12 @@ expand_builtin (exp, target, subtarget, mode, ignore) return target; break; + case BUILT_IN_STRNCPY: + target = expand_builtin_strncpy (arglist, target, mode); + if (target) + return target; + break; + case BUILT_IN_STRSTR: target = expand_builtin_strstr (arglist, target, mode); if (target) @@ -2931,6 +3076,12 @@ expand_builtin (exp, target, subtarget, mode, ignore) return target; break; + case BUILT_IN_STRNCMP: + target = expand_builtin_strncmp (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. */ diff --git a/gcc/builtins.def b/gcc/builtins.def index 1920282b280..ec4325284a3 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -39,7 +39,9 @@ DEF_BUILTIN(BUILT_IN_BCMP) DEF_BUILTIN(BUILT_IN_INDEX) DEF_BUILTIN(BUILT_IN_RINDEX) DEF_BUILTIN(BUILT_IN_STRCPY) +DEF_BUILTIN(BUILT_IN_STRNCPY) DEF_BUILTIN(BUILT_IN_STRCMP) +DEF_BUILTIN(BUILT_IN_STRNCMP) DEF_BUILTIN(BUILT_IN_STRLEN) DEF_BUILTIN(BUILT_IN_STRSTR) DEF_BUILTIN(BUILT_IN_STRPBRK) diff --git a/gcc/c-common.c b/gcc/c-common.c index e839ae507ac..d59386d96c5 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -4887,6 +4887,7 @@ c_common_nodes_and_builtins () tree int_ftype_cptr_cptr_sizet; tree int_ftype_string_string, string_ftype_ptr_ptr; tree string_ftype_string_int, string_ftype_string_string; + tree string_ftype_string_cstring_sizet, int_ftype_cstring_cstring_sizet; tree long_ftype_long; tree longlong_ftype_longlong; /* Either char* or void*. */ @@ -4987,6 +4988,14 @@ c_common_nodes_and_builtins () const_string_type_node, endlink))); + /* Prototype for strncpy. */ + string_ftype_string_cstring_sizet + = build_function_type (string_type_node, + tree_cons (NULL_TREE, string_type_node, + tree_cons (NULL_TREE, + const_string_type_node, + sizetype_endlink))); + traditional_len_type_node = ((flag_traditional && c_language != clk_cplusplus) ? integer_type_node : sizetype); @@ -5001,6 +5010,14 @@ c_common_nodes_and_builtins () const_string_type_node, endlink))); + /* Prototype for strncmp. */ + int_ftype_cstring_cstring_sizet + = build_function_type (integer_type_node, + tree_cons (NULL_TREE, const_string_type_node, + tree_cons (NULL_TREE, + const_string_type_node, + sizetype_endlink))); + /* Prototype for strstr, strpbrk, etc. */ string_ftype_string_string = build_function_type (string_type_node, @@ -5246,8 +5263,11 @@ c_common_nodes_and_builtins () BUILT_IN_INDEX, BUILT_IN_NORMAL, "index"); builtin_function ("__builtin_rindex", string_ftype_string_int, BUILT_IN_RINDEX, BUILT_IN_NORMAL, "rindex"); - builtin_function ("__builtin_strcmp", int_ftype_string_string, - BUILT_IN_STRCMP, BUILT_IN_NORMAL, "strcmp"); + built_in_decls[BUILT_IN_STRCMP] = + builtin_function ("__builtin_strcmp", int_ftype_string_string, + BUILT_IN_STRCMP, BUILT_IN_NORMAL, "strcmp"); + builtin_function ("__builtin_strncmp", int_ftype_cstring_cstring_sizet, + BUILT_IN_STRNCMP, BUILT_IN_NORMAL, "strncmp"); builtin_function ("__builtin_strstr", string_ftype_string_string, BUILT_IN_STRSTR, BUILT_IN_NORMAL, "strstr"); builtin_function ("__builtin_strpbrk", string_ftype_string_string, @@ -5259,6 +5279,8 @@ c_common_nodes_and_builtins () 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_strncpy", string_ftype_string_cstring_sizet, + BUILT_IN_STRNCPY, BUILT_IN_NORMAL, "strncpy"); builtin_function ("__builtin_strlen", strlen_ftype, BUILT_IN_STRLEN, BUILT_IN_NORMAL, "strlen"); builtin_function ("__builtin_sqrtf", float_ftype_float, @@ -5325,6 +5347,8 @@ c_common_nodes_and_builtins () BUILT_IN_NORMAL, NULL_PTR); builtin_function ("strcmp", int_ftype_string_string, BUILT_IN_STRCMP, BUILT_IN_NORMAL, NULL_PTR); + builtin_function ("strncmp", int_ftype_cstring_cstring_sizet, + BUILT_IN_STRNCMP, 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, @@ -5335,6 +5359,8 @@ c_common_nodes_and_builtins () BUILT_IN_NORMAL, NULL_PTR); builtin_function ("strcpy", string_ftype_ptr_ptr, BUILT_IN_STRCPY, BUILT_IN_NORMAL, NULL_PTR); + builtin_function ("strncpy", string_ftype_string_cstring_sizet, + BUILT_IN_STRNCPY, BUILT_IN_NORMAL, NULL_PTR); builtin_function ("strlen", strlen_ftype, BUILT_IN_STRLEN, BUILT_IN_NORMAL, NULL_PTR); builtin_function ("sqrtf", float_ftype_float, BUILT_IN_FSQRT, diff --git a/gcc/extend.texi b/gcc/extend.texi index 0d88bdf175e..0efab127c21 100644 --- a/gcc/extend.texi +++ b/gcc/extend.texi @@ -3294,6 +3294,8 @@ function as well. @findex strcmp @findex strcpy @findex strlen +@findex strncmp +@findex strncpy @findex strpbrk @findex strrchr @findex strstr @@ -3338,10 +3340,11 @@ The following ISO C89 functions are recognized as builtins unless @samp{-fno-builtin} is specified: @code{abs}, @code{cos}, @code{fabs}, @code{fputs}, @code{labs}, @code{memcmp}, @code{memcpy}, @code{memset}, @code{printf}, @code{sin}, @code{sqrt}, @code{strchr}, @code{strcmp}, -@code{strcpy}, @code{strlen}, @code{strpbrk}, @code{strrchr}, and -@code{strstr}. All of these functions have corresponding versions -prefixed with @code{__builtin_}, except that the version for @code{sqrt} -is called @code{__builtin_fsqrt}. +@code{strcpy}, @code{strlen}, @code{strncmp}, @code{strncpy}, +@code{strpbrk}, @code{strrchr}, and @code{strstr}. All of these +functions have corresponding versions prefixed with @code{__builtin_}, +except that the version for @code{sqrt} is called +@code{__builtin_fsqrt}. GNU CC provides builtin versions of the ISO C99 floating point comparison macros (that avoid raising exceptions for unordered diff --git a/gcc/testsuite/gcc.c-torture/execute/string-opt-7.c b/gcc/testsuite/gcc.c-torture/execute/string-opt-7.c new file mode 100644 index 00000000000..105b3dc95ba --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/string-opt-7.c @@ -0,0 +1,71 @@ +/* Copyright (C) 2000 Free Software Foundation. + + Ensure all expected transformations of builtin strncpy occur and + perform correctly. + + Written by Kaveh R. Ghazi, 11/25/2000. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern char *strncpy (char *, const char *, size_t); +extern int strcmp (const char *, const char *); +extern int strncmp (const char *, const char *, size_t); +extern void *memset (void *, int, size_t); + +int main () +{ + const char *const src = "hello world"; + const char *src2; + char dst[64], *dst2; + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst, src, 4) != dst || strncmp (dst, src, 4)) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst+16, src, 4) != dst+16 || strncmp (dst+16, src, 4)) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst+32, src+5, 4) != dst+32 || strncmp (dst+32, src+5, 4)) + abort(); + + memset (dst, 0, sizeof (dst)); + dst2 = dst; + if (strncpy (++dst2, src+5, 4) != dst+1 || strncmp (dst2, src+5, 4) + || dst2 != dst+1) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst, src, 0) != dst || strcmp (dst, "")) + abort(); + + memset (dst, 0, sizeof (dst)); + dst2 = dst; src2 = src; + if (strncpy (++dst2, ++src2, 0) != dst+1 || strcmp (dst2, "") + || dst2 != dst+1 || src2 != src+1) + abort(); + + memset (dst, 0, sizeof (dst)); + dst2 = dst; src2 = src; + if (strncpy (++dst2+5, ++src2+5, 0) != dst+6 || strcmp (dst2+5, "") + || dst2 != dst+1 || src2 != src+1) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst, src, 12) != dst || strcmp (dst, src)) + 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. */ +static char * +strncpy(char *s1, const char *s2, size_t n) +{ + abort(); +} +#endif diff --git a/gcc/testsuite/gcc.c-torture/execute/string-opt-8.c b/gcc/testsuite/gcc.c-torture/execute/string-opt-8.c new file mode 100644 index 00000000000..ca386f0ab37 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/string-opt-8.c @@ -0,0 +1,60 @@ +/* Copyright (C) 2000 Free Software Foundation. + + Ensure all expected transformations of builtin strncmp occur and + perform correctly. + + Written by Kaveh R. Ghazi, 11/26/2000. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern int strncmp (const char *, const char *, size_t); + +int main () +{ + const char *const s1 = "hello world"; + const char *s2, *s3; + + if (strncmp (s1, "hello world", 12) != 0) + abort(); + if (strncmp ("hello world", s1, 12) != 0) + abort(); + if (strncmp ("hello", "hello", 6) != 0) + abort(); + if (strncmp ("hello", "hello", 2) != 0) + abort(); + if (strncmp ("hello", "hello", 100) != 0) + abort(); + if (strncmp (s1+10, "d", 100) != 0) + abort(); + if (strncmp (10+s1, "d", 100) != 0) + abort(); + if (strncmp ("d", s1+10, 1) != 0) + abort(); + if (strncmp ("d", 10+s1, 1) != 0) + abort(); + if (strncmp ("hello", "aaaaa", 100) <= 0) + abort(); + if (strncmp ("aaaaa", "hello", 100) >= 0) + abort(); + if (strncmp ("hello", "aaaaa", 1) <= 0) + abort(); + if (strncmp ("aaaaa", "hello", 1) >= 0) + abort(); + + s2 = s1; s3 = s1+4; + if (strncmp (++s2, ++s3, 0) != 0 || s2 != s1+1 || s3 != s1+5) + 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. */ +static char * +strncmp(const char *s1, const char *s2, size_t n) +{ + abort(); +} +#endif -- 2.30.2