aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaveh R. Ghazi <ghazi@caip.rutgers.edu>2000-11-27 05:00:06 +0000
committerKaveh Ghazi <ghazi@gcc.gnu.org>2000-11-27 05:00:06 +0000
commitda9e9f0862f6ba1c843d2241329a368b509a450b (patch)
tree415bfe1eb788d734afb9ccfea25ebbb397482eff
parent231db5f4ec6703b6b9a931fa37c0eab18d4d49d8 (diff)
downloadgcc-da9e9f0862f6ba1c843d2241329a368b509a450b.zip
gcc-da9e9f0862f6ba1c843d2241329a368b509a450b.tar.gz
gcc-da9e9f0862f6ba1c843d2241329a368b509a450b.tar.bz2
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
-rw-r--r--gcc/ChangeLog13
-rw-r--r--gcc/builtins.c151
-rw-r--r--gcc/builtins.def2
-rw-r--r--gcc/c-common.c30
-rw-r--r--gcc/extend.texi11
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/string-opt-7.c71
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/string-opt-8.c60
7 files changed, 332 insertions, 6 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b6e8264..44ad079a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,16 @@
+2000-11-26 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
+
+ * 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 <mark@codesourcery.com>
* config/elfos.h (ASM_OUTPUT_SECTION_NAME): Use a hash table, not
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 221cba6..7db9b26 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 1920282..ec43252 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 e839ae5..d59386d 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 0d88bdf..0efab12 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 0000000..105b3dc
--- /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 0000000..ca386f0
--- /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