+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
#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));
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. */
#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. */
|| 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
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)
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. */
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)
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*. */
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);
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,
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,
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,
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,
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,
@findex strcmp
@findex strcpy
@findex strlen
+@findex strncmp
+@findex strncpy
@findex strpbrk
@findex strrchr
@findex strstr
@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
--- /dev/null
+/* 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
--- /dev/null
+/* 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