* expr.h (store_by_pieces): Add prototype.
(can_store_by_pieces): Likewise.
* expr.c (struct store_by_pieces): Renamed from clear_by_pieces.
(can_store_by_pieces): New.
(store_by_pieces): New.
(clear_by_pieces): New.
(clear_by_pieces_1): New.
(store_by_pieces_1): Renamed from clear_by_pieces, handle storing
arbitrary compiler generated constants into memory block.
(store_by_pieces_2): Renamed from clear_by_pieces_1, likewise.
* builtins.c (c_readstr): New.
(builtin_memcpy_read_str): New.
(expand_builtin_memcpy): If src is string constant and
emit_block_move would move it by pieces, compute integer constants
from the string and store it into memory block instead.
(builtin_strncpy_read_str): New.
(expand_builtin_strncpy): If N is not constant zero and c_strlen does
not return INTEGER_CST, don't optimize.
If N is larger than strlen(src) + 1, try to copy the string
including padding with store_by_pieces.
(expand_builtin_strcmp): If both arguments have side effects, don't
optimize.
(expand_builtin_fputs): If STR has side effects, don't optimize.
* gcc.c-torture/execute/string-opt-5.c: Add some strcmp and strncpy
tests.
* gcc.c-torture/execute/string-opt-6.c: New test.
From-SVN: r37851
+2000-11-29 Jakub Jelinek <jakub@redhat.com>
+
+ * expr.h (store_by_pieces): Add prototype.
+ (can_store_by_pieces): Likewise.
+ * expr.c (struct store_by_pieces): Renamed from clear_by_pieces.
+ (can_store_by_pieces): New.
+ (store_by_pieces): New.
+ (clear_by_pieces): New.
+ (clear_by_pieces_1): New.
+ (store_by_pieces_1): Renamed from clear_by_pieces, handle storing
+ arbitrary compiler generated constants into memory block.
+ (store_by_pieces_2): Renamed from clear_by_pieces_1, likewise.
+ * builtins.c (c_readstr): New.
+ (builtin_memcpy_read_str): New.
+ (expand_builtin_memcpy): If src is string constant and
+ emit_block_move would move it by pieces, compute integer constants
+ from the string and store it into memory block instead.
+ (builtin_strncpy_read_str): New.
+ (expand_builtin_strncpy): If N is not constant zero and c_strlen does
+ not return INTEGER_CST, don't optimize.
+ If N is larger than strlen(src) + 1, try to copy the string
+ including padding with store_by_pieces.
+ (expand_builtin_strcmp): If both arguments have side effects, don't
+ optimize.
+ (expand_builtin_fputs): If STR has side effects, don't optimize.
+
2000-11-29 Richard Earnshaw <rearnsha@arm.com>
* arm.md (sibcall, sibcall_value, sibcall_insn, sibcall_value_insn):
static int get_pointer_alignment PARAMS ((tree, unsigned));
static tree c_strlen PARAMS ((tree));
static const char *c_getstr PARAMS ((tree));
+static rtx c_readstr PARAMS ((const char *,
+ enum machine_mode));
static rtx get_memory_rtx PARAMS ((tree));
static int apply_args_size PARAMS ((void));
static int apply_result_size PARAMS ((void));
enum machine_mode));
static rtx expand_builtin_strncmp PARAMS ((tree, rtx,
enum machine_mode));
+static rtx builtin_memcpy_read_str PARAMS ((PTR, HOST_WIDE_INT,
+ enum machine_mode));
static rtx expand_builtin_memcpy PARAMS ((tree));
static rtx expand_builtin_strcpy PARAMS ((tree));
+static rtx builtin_strncpy_read_str PARAMS ((PTR, HOST_WIDE_INT,
+ enum machine_mode));
static rtx expand_builtin_strncpy PARAMS ((tree, rtx,
enum machine_mode));
static rtx expand_builtin_memset PARAMS ((tree));
return ptr + offset;
}
+/* Return a CONST_INT or CONST_DOUBLE corresponding to target
+ reading GET_MODE_BITSIZE (MODE) bits from string constant
+ STR. */
+static rtx
+c_readstr (str, mode)
+ const char *str;
+ enum machine_mode mode;
+{
+ HOST_WIDE_INT c[2];
+ HOST_WIDE_INT ch;
+ unsigned int i, j;
+
+ if (GET_MODE_CLASS (mode) != MODE_INT)
+ abort ();
+ c[0] = 0;
+ c[1] = 0;
+ ch = 1;
+ for (i = 0; i < GET_MODE_SIZE (mode); i++)
+ {
+ j = i;
+ if (WORDS_BIG_ENDIAN)
+ j = GET_MODE_SIZE (mode) - i - 1;
+ if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
+ && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+ j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1;
+ j *= BITS_PER_UNIT;
+ if (j > 2 * HOST_BITS_PER_WIDE_INT)
+ abort ();
+ if (ch)
+ ch = (unsigned char) str[i];
+ c[j / HOST_BITS_PER_WIDE_INT] |= ch << (j % HOST_BITS_PER_WIDE_INT);
+ }
+ return immed_double_const (c[0], c[1], mode);
+}
+
/* 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). */
}
}
+/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE)
+ bytes from constant string DATA + OFFSET and return it as target
+ constant. */
+
+static rtx
+builtin_memcpy_read_str (data, offset, mode)
+ PTR data;
+ HOST_WIDE_INT offset;
+ enum machine_mode mode;
+{
+ const char *str = (const char *) data;
+
+ if (offset + GET_MODE_SIZE (mode) > strlen (str) + 1)
+ abort (); /* Attempt to read past the end of constant string. */
+
+ return c_readstr (str + offset, mode);
+}
+
/* Expand a call to the memcpy builtin, with arguments in ARGLIST. */
static rtx
expand_builtin_memcpy (arglist)
tree dest = TREE_VALUE (arglist);
tree src = TREE_VALUE (TREE_CHAIN (arglist));
tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ const char *src_str;
int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
return 0;
dest_mem = get_memory_rtx (dest);
- src_mem = get_memory_rtx (src);
len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+ src_str = c_getstr (src);
+
+ /* If SRC is a string constant and block move would be done
+ by pieces, we can avoid loading the string from memory
+ and only stored the computed constants. */
+ if (src_str
+ && !current_function_check_memory_usage
+ && GET_CODE (len_rtx) == CONST_INT
+ && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
+ && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
+ (PTR) src_str, dest_align))
+ {
+ store_by_pieces (dest_mem, INTVAL (len_rtx),
+ builtin_memcpy_read_str,
+ (PTR) src_str, dest_align);
+ return force_operand (XEXP (dest_mem, 0), NULL_RTX);
+ }
+
+ src_mem = get_memory_rtx (src);
/* Just copy the rights of SRC to the rights of DEST. */
if (current_function_check_memory_usage)
return result;
}
+/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE)
+ bytes from constant string DATA + OFFSET and return it as target
+ constant. */
+
+static rtx
+builtin_strncpy_read_str (data, offset, mode)
+ PTR data;
+ HOST_WIDE_INT offset;
+ enum machine_mode mode;
+{
+ const char *str = (const char *) data;
+
+ if ((unsigned HOST_WIDE_INT) offset > strlen (str))
+ return const0_rtx;
+
+ return c_readstr (str + offset, mode);
+}
+
/* Expand expression EXP, which is a call to the strncpy builtin. Return 0
if we failed the caller should emit a normal call. */
return expand_expr (TREE_VALUE (arglist), target, mode,
EXPAND_NORMAL);
}
-
+
/* Now, we must be passed a constant src ptr parameter. */
- if (slen == 0)
+ if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
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. */
+ len is greater than strlen(s2)+1. In that case try to
+ use store_by_pieces, if it fails, punt. */
if (tree_int_cst_lt (slen, len))
- return 0;
+ {
+ tree dest = TREE_VALUE (arglist);
+ int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+ const char *p = c_getstr (TREE_VALUE (TREE_CHAIN (arglist)));
+ rtx dest_mem;
+
+ if (!p || !dest_align || TREE_INT_CST_HIGH (len)
+ || !can_store_by_pieces (TREE_INT_CST_LOW (len),
+ builtin_strncpy_read_str,
+ (PTR) p, dest_align))
+ return 0;
+
+ dest_mem = get_memory_rtx (dest);
+ store_by_pieces (dest_mem, TREE_INT_CST_LOW (len),
+ builtin_strncpy_read_str,
+ (PTR) p, dest_align);
+ return force_operand (XEXP (dest_mem, 0), NULL_RTX);
+ }
/* OK transform into builtin memcpy. */
return expand_builtin_memcpy (arglist);
/* If we don't have a constant length for the first, use the length
of the second, if we know it. We don't require a constant for
this case; some cost analysis could be done if both are available
- but neither is constant. For now, assume they're equally cheap.
+ but neither is constant. For now, assume they're equally cheap
+ unless one has side effects.
If both strings have constant lengths, use the smaller. This
could arise if optimization results in strcpy being called with
if (!len || TREE_CODE (len) != INTEGER_CST)
{
- if (len2)
+ if (len2 && !TREE_SIDE_EFFECTS (len2))
len = len2;
else if (len == 0)
return 0;
&& tree_int_cst_lt (len2, len))
len = len2;
+ /* If both arguments have side effects, we cannot optimize. */
+ if (TREE_SIDE_EFFECTS (len))
+ return 0;
+
chainon (arglist, build_tree_list (NULL_TREE, len));
result = expand_builtin_memcmp (exp, arglist, target);
if (! result)
/* Get the length of the string passed to fputs. If the length
can't be determined, punt. */
- if (!(len = c_strlen (TREE_VALUE (arglist))))
+ if (!(len = c_strlen (TREE_VALUE (arglist)))
+ || TREE_CODE (len) != INTEGER_CST)
return 0;
switch (compare_tree_int (len, 1))
int reverse;
};
-/* This structure is used by clear_by_pieces to describe the clear to
+/* This structure is used by store_by_pieces to describe the clear to
be performed. */
-struct clear_by_pieces
+struct store_by_pieces
{
rtx to;
rtx to_addr;
int explicit_inc_to;
unsigned HOST_WIDE_INT len;
HOST_WIDE_INT offset;
+ rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
+ PTR constfundata;
int reverse;
};
unsigned int));
static void move_by_pieces_1 PARAMS ((rtx (*) (rtx, ...), enum machine_mode,
struct move_by_pieces *));
+static rtx clear_by_pieces_1 PARAMS ((PTR, HOST_WIDE_INT,
+ enum machine_mode));
static void clear_by_pieces PARAMS ((rtx, unsigned HOST_WIDE_INT,
unsigned int));
-static void clear_by_pieces_1 PARAMS ((rtx (*) (rtx, ...),
+static void store_by_pieces_1 PARAMS ((struct store_by_pieces *,
+ unsigned int));
+static void store_by_pieces_2 PARAMS ((rtx (*) (rtx, ...),
enum machine_mode,
- struct clear_by_pieces *));
+ struct store_by_pieces *));
static rtx get_subtarget PARAMS ((rtx));
static int is_zeros_p PARAMS ((tree));
static int mostly_zeros_p PARAMS ((tree));
}
}
\f
+
+int
+can_store_by_pieces (len, constfun, constfundata, align)
+ unsigned HOST_WIDE_INT len;
+ rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
+ PTR constfundata;
+ unsigned int align;
+{
+ unsigned HOST_WIDE_INT max_size = MOVE_MAX_PIECES + 1, l;
+ HOST_WIDE_INT offset = 0;
+ enum machine_mode mode, tmode;
+ enum insn_code icode;
+ int reverse;
+ rtx cst;
+
+ if (! MOVE_BY_PIECES_P (len, align))
+ return 0;
+
+ if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
+ || align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
+ align = MOVE_MAX * BITS_PER_UNIT;
+
+ /* We would first store what we can in the largest integer mode, then go to
+ successively smaller modes. */
+
+ for (reverse = 0;
+ reverse <= (HAVE_PRE_DECREMENT || HAVE_POST_DECREMENT);
+ reverse++)
+ {
+ l = len;
+ mode = VOIDmode;
+ while (max_size > 1)
+ {
+ for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
+ if (GET_MODE_SIZE (tmode) < max_size)
+ mode = tmode;
+
+ if (mode == VOIDmode)
+ break;
+
+ icode = mov_optab->handlers[(int) mode].insn_code;
+ if (icode != CODE_FOR_nothing
+ && align >= GET_MODE_ALIGNMENT (mode))
+ {
+ unsigned int size = GET_MODE_SIZE (mode);
+
+ while (l >= size)
+ {
+ if (reverse)
+ offset -= size;
+
+ cst = (*constfun) (constfundata, offset, mode);
+ if (!LEGITIMATE_CONSTANT_P (cst))
+ return 0;
+
+ if (!reverse)
+ offset += size;
+
+ l -= size;
+ }
+ }
+
+ max_size = GET_MODE_SIZE (mode);
+ }
+
+ /* The code above should have handled everything. */
+ if (l != 0)
+ abort ();
+ }
+
+ return 1;
+}
+
+/* Generate several move instructions to store LEN bytes generated by
+ CONSTFUN to block TO. (A MEM rtx with BLKmode). CONSTFUNDATA is a
+ pointer which will be passed as argument in every CONSTFUN call.
+ ALIGN is maximum alignment we can assume. */
+
+void
+store_by_pieces (to, len, constfun, constfundata, align)
+ rtx to;
+ unsigned HOST_WIDE_INT len;
+ rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
+ PTR constfundata;
+ unsigned int align;
+{
+ struct store_by_pieces data;
+
+ if (! MOVE_BY_PIECES_P (len, align))
+ abort ();
+ to = protect_from_queue (to, 1);
+ data.constfun = constfun;
+ data.constfundata = constfundata;
+ data.len = len;
+ data.to = to;
+ store_by_pieces_1 (&data, align);
+}
+
/* Generate several move instructions to clear LEN bytes of block TO. (A MEM
rtx with BLKmode). The caller must pass TO through protect_from_queue
before calling. ALIGN is maximum alignment we can assume. */
unsigned HOST_WIDE_INT len;
unsigned int align;
{
- struct clear_by_pieces data;
- rtx to_addr = XEXP (to, 0);
+ struct store_by_pieces data;
+
+ data.constfun = clear_by_pieces_1;
+ data.constfundata = NULL_PTR;
+ data.len = len;
+ data.to = to;
+ store_by_pieces_1 (&data, align);
+}
+
+/* Callback routine for clear_by_pieces.
+ Return const0_rtx unconditionally. */
+
+static rtx
+clear_by_pieces_1 (data, offset, mode)
+ PTR data ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT offset ATTRIBUTE_UNUSED;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return const0_rtx;
+}
+
+/* Subroutine of clear_by_pieces and store_by_pieces.
+ Generate several move instructions to store LEN bytes of block TO. (A MEM
+ rtx with BLKmode). The caller must pass TO through protect_from_queue
+ before calling. ALIGN is maximum alignment we can assume. */
+
+static void
+store_by_pieces_1 (data, align)
+ struct store_by_pieces *data;
+ unsigned int align;
+{
+ rtx to_addr = XEXP (data->to, 0);
unsigned HOST_WIDE_INT max_size = MOVE_MAX_PIECES + 1;
enum machine_mode mode = VOIDmode, tmode;
enum insn_code icode;
- data.offset = 0;
- data.to_addr = to_addr;
- data.to = to;
- data.autinc_to
+ data->offset = 0;
+ data->to_addr = to_addr;
+ data->autinc_to
= (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
|| GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
- data.explicit_inc_to = 0;
- data.reverse
+ data->explicit_inc_to = 0;
+ data->reverse
= (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
- if (data.reverse)
- data.offset = len;
- data.len = len;
+ if (data->reverse)
+ data->offset = data->len;
- /* If copying requires more than two move insns,
+ /* If storing requires more than two move insns,
copy addresses to registers (to make displacements shorter)
and use post-increment if available. */
- if (!data.autinc_to
- && move_by_pieces_ninsns (len, align) > 2)
+ if (!data->autinc_to
+ && move_by_pieces_ninsns (data->len, align) > 2)
{
/* Determine the main mode we'll be using. */
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
if (GET_MODE_SIZE (tmode) < max_size)
mode = tmode;
- if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to)
+ if (USE_STORE_PRE_DECREMENT (mode) && data->reverse && ! data->autinc_to)
{
- data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len));
- data.autinc_to = 1;
- data.explicit_inc_to = -1;
+ data->to_addr = copy_addr_to_reg (plus_constant (to_addr, data->len));
+ data->autinc_to = 1;
+ data->explicit_inc_to = -1;
}
- if (USE_STORE_POST_INCREMENT (mode) && ! data.reverse
- && ! data.autinc_to)
+ if (USE_STORE_POST_INCREMENT (mode) && ! data->reverse
+ && ! data->autinc_to)
{
- data.to_addr = copy_addr_to_reg (to_addr);
- data.autinc_to = 1;
- data.explicit_inc_to = 1;
+ data->to_addr = copy_addr_to_reg (to_addr);
+ data->autinc_to = 1;
+ data->explicit_inc_to = 1;
}
- if ( !data.autinc_to && CONSTANT_P (to_addr))
- data.to_addr = copy_addr_to_reg (to_addr);
+ if ( !data->autinc_to && CONSTANT_P (to_addr))
+ data->to_addr = copy_addr_to_reg (to_addr);
}
if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
|| align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
align = MOVE_MAX * BITS_PER_UNIT;
- /* First move what we can in the largest integer mode, then go to
+ /* First store what we can in the largest integer mode, then go to
successively smaller modes. */
while (max_size > 1)
icode = mov_optab->handlers[(int) mode].insn_code;
if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
- clear_by_pieces_1 (GEN_FCN (icode), mode, &data);
+ store_by_pieces_2 (GEN_FCN (icode), mode, data);
max_size = GET_MODE_SIZE (mode);
}
/* The code above should have handled everything. */
- if (data.len != 0)
+ if (data->len != 0)
abort ();
}
-/* Subroutine of clear_by_pieces. Clear as many bytes as appropriate
+/* Subroutine of store_by_pieces_1. Store as many bytes as appropriate
with move instructions for mode MODE. GENFUN is the gen_... function
to make a move insn for that mode. DATA has all the other info. */
static void
-clear_by_pieces_1 (genfun, mode, data)
+store_by_pieces_2 (genfun, mode, data)
rtx (*genfun) PARAMS ((rtx, ...));
enum machine_mode mode;
- struct clear_by_pieces *data;
+ struct store_by_pieces *data;
{
unsigned int size = GET_MODE_SIZE (mode);
- rtx to1;
+ rtx to1, cst;
while (data->len >= size)
{
plus_constant (data->to_addr, data->offset));
if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
- emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size)));
+ emit_insn (gen_add2_insn (data->to_addr,
+ GEN_INT (-(HOST_WIDE_INT) size)));
- emit_insn ((*genfun) (to1, const0_rtx));
+ cst = (*data->constfun) (data->constfundata, data->offset, mode);
+ emit_insn ((*genfun) (to1, cst));
if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0)
emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
alignment. */
extern rtx clear_storage PARAMS ((rtx, rtx, unsigned int));
+/* Return non-zero if it is desirable to store LEN bytes generated by
+ CONSTFUN with several move instructions by store_by_pieces
+ function. CONSTFUNDATA is a pointer which will be passed as argument
+ in every CONSTFUN call.
+ ALIGN is maximum alignment we can assume. */
+extern int can_store_by_pieces PARAMS ((unsigned HOST_WIDE_INT,
+ rtx (*) (PTR, HOST_WIDE_INT,
+ enum machine_mode),
+ PTR, unsigned int));
+
+/* Generate several move instructions to store LEN bytes generated by
+ CONSTFUN to block TO. (A MEM rtx with BLKmode). CONSTFUNDATA is a
+ pointer which will be passed as argument in every CONSTFUN call.
+ ALIGN is maximum alignment we can assume. */
+extern void store_by_pieces PARAMS ((rtx, unsigned HOST_WIDE_INT,
+ rtx (*) (PTR, HOST_WIDE_INT,
+ enum machine_mode),
+ PTR, unsigned int));
+
/* Emit insns to set X from Y. */
extern rtx emit_move_insn PARAMS ((rtx, rtx));
+2000-11-29 Jakub Jelinek <jakub@redhat.com>
+
+ * gcc.c-torture/execute/string-opt-5.c: Add some strcmp and strncpy
+ tests.
+ * gcc.c-torture/execute/string-opt-6.c: New test.
+
2000-11-28 Geoffrey Keating <geoffk@redhat.com>
* gcc.dg/noncompile/940510-1.c: Update to test c89 functionality.
/* Copyright (C) 2000 Free Software Foundation.
- Ensure builtin strlen, strcmp, strchr and strrchr perform correctly.
+ Ensure builtin strlen, strcmp, strchr, strrchr and strncpy
+ perform correctly.
Written by Jakub Jelinek, 11/7/2000. */
extern int strcmp (const char *, const char *);
extern char *strchr (const char *, int);
extern char *strrchr (const char *, int);
+extern char *strncpy (char *, const char *, __SIZE_TYPE__);
+extern void *memset (void *, int, __SIZE_TYPE__);
+extern int memcmp (const void *, const void *, __SIZE_TYPE__);
int x = 6;
+int y = 1;
char *bar = "hi world";
int main()
{
const char *const foo = "hello world";
+ char dst [64];
if (strlen (bar) != 8)
abort ();
abort ();
if (strrchr (bar, 'o') != bar + 4)
abort ();
+ if (strcmp (foo + (x++ & 1), "ello world" + (--y & 1)))
+ abort ();
+ if (x != 6 || y != 0)
+ abort ();
+ dst[5] = ' ';
+ dst[6] = '\0';
+ x = 5;
+ y = 1;
+ if (strncpy (dst + 1, foo + (x++ & 3), 4) != dst + 1
+ || x != 6
+ || strcmp (dst + 1, "ello "))
+ abort ();
+ memset (dst, ' ', sizeof dst);
+ if (strncpy (dst + (++x & 1), (y++ & 3) + "foo", 10) != dst + 1
+ || x != 7
+ || y != 2
+ || memcmp (dst, " oo\0\0\0\0\0\0\0\0 ", 12))
+ abort ();
+ memset (dst, ' ', sizeof dst);
+ if (strncpy (dst, "hello", 8) != dst || memcmp (dst, "hello\0\0\0 ", 9))
+ abort();
return 0;
}
--- /dev/null
+/* Copyright (C) 2000 Free Software Foundation.
+
+ Ensure builtin memcpy and strcpy perform correctly.
+
+ Written by Jakub Jelinek, 11/24/2000. */
+
+extern void abort (void);
+extern char *strcpy (char *, const char *);
+typedef __SIZE_TYPE__ size_t;
+extern void *memcpy (void *, const void *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+
+char p[32] = "";
+
+int main()
+{
+ if (strcpy (p, "abcde") != p || memcmp (p, "abcde", 6))
+ abort ();
+ if (strcpy (p + 16, "vwxyz" + 1) != p + 16 || memcmp (p + 16, "wxyz", 5))
+ abort ();
+ if (strcpy (p + 1, "") != p + 1 || memcmp (p, "a\0cde", 6))
+ abort ();
+ if (strcpy (p + 3, "fghij") != p + 3 || memcmp (p, "a\0cfghij", 9))
+ abort ();
+ if (memcpy (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6))
+ abort ();
+ if (memcpy (p + 16, "VWX" + 1, 2) != p + 16 || memcmp (p + 16, "WXyz", 5))
+ abort ();
+ if (memcpy (p + 1, "", 1) != p + 1 || memcmp (p, "A\0CDE", 6))
+ abort ();
+ if (memcpy (p + 3, "FGHI", 4) != p + 3 || memcmp (p, "A\0CFGHIj", 9))
+ 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 *
+strcpy (char *d, const char *s)
+{
+ abort ();
+}
+#endif