re PR middle-end/21265 (GCC fails to optimize tail call to memset)

PR middle-end/21265
	* expr.h (enum block_op_methods): Add BLOCK_OP_TAILCALL.
	(clear_storage): Add argument.
	* expr.c (emit_block_move_via_libcall, clear_storage_via_libcall):
	Add tailcall argument, set CALL_EXPR_TAILCALL of the CALL_EXPR to
	tailcall.
	(emit_block_move): Handle BLOCK_OP_TAILCALL method.
	(clear_storage): Add method argument, handle BLOCK_OP_TAILCALL.
	(store_expr, store_constructor): Adjust callers.
	* builtins.c (expand_builtin_memcpy): Pass BLOCK_OP_TAILCALL
	to emit_block_move if CALL_EXPR_TAILCALL (exp).
	(expand_builtin_memmove): Add ORIG_EXP argument, copy
	CALL_EXPR_TAILCALL from ORIG_EXP to the new CALL_EXPR.
	(expand_builtin_bcopy): Replace ARGLIST and TYPE arguments
	with EXP.  Pass EXP to expand_builtin_memmove.
	(expand_builtin_memset): Add ORIG_EXP argument, pass
	BLOCK_OP_TAILCALL to clear_storage if CALL_EXPR_TAILCALL (orig_exp).
	(expand_builtin_bzero): Replace ARGLIST argument with EXP.
	Pass EXP to expand_builtin_memset.
	(expand_builtin_strcmp): Copy CALL_EXPR_TAILCALL from EXP to
	the new CALL_EXPR.
	(expand_builtin_strncmp): Likewise.
	(expand_builtin_printf): Replace ARGLIST argument with EXP.
	Copy CALL_EXPR_TAILCALL from EXP to the new CALL_EXPR.
	(expand_builtin_fprintf): Likewise.
	(expand_builtin): Adjust calls to
	expand_builtin_{memmove,bcopy,memset,bzero,{,f}printf}.

	* gcc.dg/20050503-1.c: New test.

From-SVN: r99187
This commit is contained in:
Jakub Jelinek 2005-05-04 00:22:02 +02:00 committed by Jakub Jelinek
parent 125886c749
commit 8148fe656d
6 changed files with 159 additions and 50 deletions

View File

@ -1,5 +1,33 @@
2005-05-04 Jakub Jelinek <jakub@redhat.com>
PR middle-end/21265
* expr.h (enum block_op_methods): Add BLOCK_OP_TAILCALL.
(clear_storage): Add argument.
* expr.c (emit_block_move_via_libcall, clear_storage_via_libcall):
Add tailcall argument, set CALL_EXPR_TAILCALL of the CALL_EXPR to
tailcall.
(emit_block_move): Handle BLOCK_OP_TAILCALL method.
(clear_storage): Add method argument, handle BLOCK_OP_TAILCALL.
(store_expr, store_constructor): Adjust callers.
* builtins.c (expand_builtin_memcpy): Pass BLOCK_OP_TAILCALL
to emit_block_move if CALL_EXPR_TAILCALL (exp).
(expand_builtin_memmove): Add ORIG_EXP argument, copy
CALL_EXPR_TAILCALL from ORIG_EXP to the new CALL_EXPR.
(expand_builtin_bcopy): Replace ARGLIST and TYPE arguments
with EXP. Pass EXP to expand_builtin_memmove.
(expand_builtin_memset): Add ORIG_EXP argument, pass
BLOCK_OP_TAILCALL to clear_storage if CALL_EXPR_TAILCALL (orig_exp).
(expand_builtin_bzero): Replace ARGLIST argument with EXP.
Pass EXP to expand_builtin_memset.
(expand_builtin_strcmp): Copy CALL_EXPR_TAILCALL from EXP to
the new CALL_EXPR.
(expand_builtin_strncmp): Likewise.
(expand_builtin_printf): Replace ARGLIST argument with EXP.
Copy CALL_EXPR_TAILCALL from EXP to the new CALL_EXPR.
(expand_builtin_fprintf): Likewise.
(expand_builtin): Adjust calls to
expand_builtin_{memmove,bcopy,memset,bzero,{,f}printf}.
* config/i386/i386.c (ix86_expand_vector_set): Fix setting 3rd and 4th
item in V4SF mode.

View File

@ -111,15 +111,15 @@ static rtx expand_builtin_strspn (tree, rtx, enum machine_mode);
static rtx expand_builtin_strcspn (tree, rtx, enum machine_mode);
static rtx expand_builtin_memcpy (tree, rtx, enum machine_mode);
static rtx expand_builtin_mempcpy (tree, tree, rtx, enum machine_mode, int);
static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode);
static rtx expand_builtin_bcopy (tree, tree);
static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode, tree);
static rtx expand_builtin_bcopy (tree);
static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode);
static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode);
static rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode);
static rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode);
static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode);
static rtx expand_builtin_memset (tree, rtx, enum machine_mode);
static rtx expand_builtin_memset (tree, rtx, enum machine_mode, tree);
static rtx expand_builtin_bzero (tree);
static rtx expand_builtin_strlen (tree, rtx, enum machine_mode);
static rtx expand_builtin_strstr (tree, tree, rtx, enum machine_mode);
@ -2808,7 +2808,8 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
/* Copy word part most expediently. */
dest_addr = emit_block_move (dest_mem, src_mem, len_rtx,
BLOCK_OP_NORMAL);
CALL_EXPR_TAILCALL (exp)
? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
if (dest_addr == 0)
{
@ -2915,7 +2916,7 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m
static rtx
expand_builtin_memmove (tree arglist, tree type, rtx target,
enum machine_mode mode)
enum machine_mode mode, tree orig_exp)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
@ -2947,11 +2948,13 @@ expand_builtin_memmove (tree arglist, tree type, rtx target,
normal memcpy. */
if (readonly_data_expr (src))
{
tree const fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
if (!fn)
return 0;
return expand_expr (build_function_call_expr (fn, arglist),
target, mode, EXPAND_NORMAL);
fn = build_function_call_expr (fn, arglist);
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp);
return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
/* If length is 1 and we can expand memcpy call inline,
@ -2973,8 +2976,10 @@ expand_builtin_memmove (tree arglist, tree type, rtx target,
if we failed the caller should emit a normal call. */
static rtx
expand_builtin_bcopy (tree arglist, tree type)
expand_builtin_bcopy (tree exp)
{
tree arglist = TREE_OPERAND (exp, 1);
tree type = TREE_TYPE (exp);
tree src, dest, size, newarglist;
if (!validate_arglist (arglist,
@ -2994,7 +2999,7 @@ expand_builtin_bcopy (tree arglist, tree type)
newarglist = tree_cons (NULL_TREE, src, newarglist);
newarglist = tree_cons (NULL_TREE, dest, newarglist);
return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode);
return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp);
}
#ifndef HAVE_movstr
@ -3288,7 +3293,8 @@ builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
convenient). */
static rtx
expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode,
tree orig_exp)
{
if (!validate_arglist (arglist,
POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
@ -3374,7 +3380,9 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
dest_mem = get_memory_rtx (dest);
set_mem_align (dest_mem, dest_align);
dest_addr = clear_storage (dest_mem, len_rtx);
dest_addr = clear_storage (dest_mem, len_rtx,
CALL_EXPR_TAILCALL (orig_exp)
? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
if (dest_addr == 0)
{
@ -3390,8 +3398,9 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
if we failed the caller should emit a normal call. */
static rtx
expand_builtin_bzero (tree arglist)
expand_builtin_bzero (tree exp)
{
tree arglist = TREE_OPERAND (exp, 1);
tree dest, size, newarglist;
if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
@ -3409,7 +3418,7 @@ expand_builtin_bzero (tree arglist)
newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist);
newarglist = tree_cons (NULL_TREE, dest, newarglist);
return expand_builtin_memset (newarglist, const0_rtx, VOIDmode);
return expand_builtin_memset (newarglist, const0_rtx, VOIDmode, exp);
}
/* Expand expression EXP, which is a call to the memcmp built-in function.
@ -3548,7 +3557,7 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
tree fndecl;
tree fndecl, fn;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@ -3632,8 +3641,10 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
arglist = build_tree_list (NULL_TREE, arg2);
arglist = tree_cons (NULL_TREE, arg1, arglist);
fndecl = get_callee_fndecl (exp);
exp = build_function_call_expr (fndecl, arglist);
return expand_call (exp, target, target == const0_rtx);
fn = build_function_call_expr (fndecl, arglist);
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_call (fn, target, target == const0_rtx);
}
#endif
return 0;
@ -3670,7 +3681,7 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
tree fndecl;
tree fndecl, fn;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@ -3760,8 +3771,10 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
arglist = tree_cons (NULL_TREE, arg2, arglist);
arglist = tree_cons (NULL_TREE, arg1, arglist);
fndecl = get_callee_fndecl (exp);
exp = build_function_call_expr (fndecl, arglist);
return expand_call (exp, target, target == const0_rtx);
fn = build_function_call_expr (fndecl, arglist);
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_call (fn, target, target == const0_rtx);
}
#endif
return 0;
@ -4693,15 +4706,16 @@ build_string_literal (int len, const char *str)
return t;
}
/* Expand a call to printf or printf_unlocked with argument list ARGLIST.
/* Expand EXP, a call to printf or printf_unlocked.
Return 0 if a normal call should be emitted rather than transforming
the function inline. If convenient, the result should be placed in
TARGET with mode MODE. UNLOCKED indicates this is a printf_unlocked
call. */
static rtx
expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode,
expand_builtin_printf (tree exp, rtx target, enum machine_mode mode,
bool unlocked)
{
tree arglist = TREE_OPERAND (exp, 1);
tree fn_putchar = unlocked
? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
: implicit_built_in_decls[BUILT_IN_PUTCHAR];
@ -4792,19 +4806,22 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode,
if (!fn)
return 0;
return expand_expr (build_function_call_expr (fn, arglist),
target, mode, EXPAND_NORMAL);
fn = build_function_call_expr (fn, arglist);
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST.
/* Expand EXP, a call to fprintf or fprintf_unlocked.
Return 0 if a normal call should be emitted rather than transforming
the function inline. If convenient, the result should be placed in
TARGET with mode MODE. UNLOCKED indicates this is a fprintf_unlocked
call. */
static rtx
expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode,
expand_builtin_fprintf (tree exp, rtx target, enum machine_mode mode,
bool unlocked)
{
tree arglist = TREE_OPERAND (exp, 1);
tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
: implicit_built_in_decls[BUILT_IN_FPUTC];
tree fn_fputs = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED]
@ -4886,8 +4903,10 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode,
if (!fn)
return 0;
return expand_expr (build_function_call_expr (fn, arglist),
target, mode, EXPAND_NORMAL);
fn = build_function_call_expr (fn, arglist);
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
/* Expand a call to sprintf with argument list ARGLIST. Return 0 if
@ -5850,25 +5869,26 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
break;
case BUILT_IN_MEMMOVE:
target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target, mode);
target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target,
mode, exp);
if (target)
return target;
break;
case BUILT_IN_BCOPY:
target = expand_builtin_bcopy (arglist, TREE_TYPE (exp));
target = expand_builtin_bcopy (exp);
if (target)
return target;
break;
case BUILT_IN_MEMSET:
target = expand_builtin_memset (arglist, target, mode);
target = expand_builtin_memset (arglist, target, mode, exp);
if (target)
return target;
break;
case BUILT_IN_BZERO:
target = expand_builtin_bzero (arglist);
target = expand_builtin_bzero (exp);
if (target)
return target;
break;
@ -5945,13 +5965,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
return const0_rtx;
case BUILT_IN_PRINTF:
target = expand_builtin_printf (arglist, target, mode, false);
target = expand_builtin_printf (exp, target, mode, false);
if (target)
return target;
break;
case BUILT_IN_PRINTF_UNLOCKED:
target = expand_builtin_printf (arglist, target, mode, true);
target = expand_builtin_printf (exp, target, mode, true);
if (target)
return target;
break;
@ -5968,13 +5988,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
break;
case BUILT_IN_FPRINTF:
target = expand_builtin_fprintf (arglist, target, mode, false);
target = expand_builtin_fprintf (exp, target, mode, false);
if (target)
return target;
break;
case BUILT_IN_FPRINTF_UNLOCKED:
target = expand_builtin_fprintf (arglist, target, mode, true);
target = expand_builtin_fprintf (exp, target, mode, true);
if (target)
return target;
break;

View File

@ -126,7 +126,7 @@ static void move_by_pieces_1 (rtx (*) (rtx, ...), enum machine_mode,
struct move_by_pieces *);
static bool block_move_libcall_safe_for_call_parm (void);
static bool emit_block_move_via_movmem (rtx, rtx, rtx, unsigned);
static rtx emit_block_move_via_libcall (rtx, rtx, rtx);
static rtx emit_block_move_via_libcall (rtx, rtx, rtx, bool);
static tree emit_block_move_libcall_fn (int);
static void emit_block_move_via_loop (rtx, rtx, rtx, unsigned);
static rtx clear_by_pieces_1 (void *, HOST_WIDE_INT, enum machine_mode);
@ -135,7 +135,7 @@ static void store_by_pieces_1 (struct store_by_pieces *, unsigned int);
static void store_by_pieces_2 (rtx (*) (rtx, ...), enum machine_mode,
struct store_by_pieces *);
static bool clear_storage_via_clrmem (rtx, rtx, unsigned);
static rtx clear_storage_via_libcall (rtx, rtx);
static rtx clear_storage_via_libcall (rtx, rtx, bool);
static tree clear_storage_libcall_fn (int);
static rtx compress_float_constant (rtx, rtx);
static rtx get_subtarget (rtx);
@ -1148,6 +1148,7 @@ emit_block_move (rtx x, rtx y, rtx size, enum block_op_methods method)
switch (method)
{
case BLOCK_OP_NORMAL:
case BLOCK_OP_TAILCALL:
may_use_call = true;
break;
@ -1196,7 +1197,8 @@ emit_block_move (rtx x, rtx y, rtx size, enum block_op_methods method)
else if (emit_block_move_via_movmem (x, y, size, align))
;
else if (may_use_call)
retval = emit_block_move_via_libcall (x, y, size);
retval = emit_block_move_via_libcall (x, y, size,
method == BLOCK_OP_TAILCALL);
else
emit_block_move_via_loop (x, y, size, align);
@ -1325,7 +1327,7 @@ emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align)
Return the return value from memcpy, 0 otherwise. */
static rtx
emit_block_move_via_libcall (rtx dst, rtx src, rtx size)
emit_block_move_via_libcall (rtx dst, rtx src, rtx size, bool tailcall)
{
rtx dst_addr, src_addr;
tree call_expr, arg_list, fn, src_tree, dst_tree, size_tree;
@ -1367,6 +1369,7 @@ emit_block_move_via_libcall (rtx dst, rtx src, rtx size)
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arg_list, NULL_TREE);
CALL_EXPR_TAILCALL (call_expr) = tailcall;
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
@ -2427,11 +2430,13 @@ store_by_pieces_2 (rtx (*genfun) (rtx, ...), enum machine_mode mode,
its length in bytes. */
rtx
clear_storage (rtx object, rtx size)
clear_storage (rtx object, rtx size, enum block_op_methods method)
{
enum machine_mode mode = GET_MODE (object);
unsigned int align;
gcc_assert (method == BLOCK_OP_NORMAL || method == BLOCK_OP_TAILCALL);
/* If OBJECT is not BLKmode and SIZE is the same size as its mode,
just move a zero. Otherwise, do this a piece at a time. */
if (mode != BLKmode
@ -2468,7 +2473,8 @@ clear_storage (rtx object, rtx size)
else if (clear_storage_via_clrmem (object, size, align))
;
else
return clear_storage_via_libcall (object, size);
return clear_storage_via_libcall (object, size,
method == BLOCK_OP_TAILCALL);
return NULL;
}
@ -2533,7 +2539,7 @@ clear_storage_via_clrmem (rtx object, rtx size, unsigned int align)
Return the return value of memset, 0 otherwise. */
static rtx
clear_storage_via_libcall (rtx object, rtx size)
clear_storage_via_libcall (rtx object, rtx size, bool tailcall)
{
tree call_expr, arg_list, fn, object_tree, size_tree;
enum machine_mode size_mode;
@ -2566,6 +2572,7 @@ clear_storage_via_libcall (rtx object, rtx size)
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arg_list, NULL_TREE);
CALL_EXPR_TAILCALL (call_expr) = tailcall;
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
@ -4305,7 +4312,7 @@ store_expr (tree exp, rtx target, int call_param_p)
}
if (size != const0_rtx)
clear_storage (target, size);
clear_storage (target, size, BLOCK_OP_NORMAL);
if (label)
emit_label (label);
@ -4659,7 +4666,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
&& ! CONSTRUCTOR_ELTS (exp))
/* If the constructor is empty, clear the union. */
{
clear_storage (target, expr_size (exp));
clear_storage (target, expr_size (exp), BLOCK_OP_NORMAL);
cleared = 1;
}
@ -4687,7 +4694,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
|| ((HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (target))
== size)))
{
clear_storage (target, GEN_INT (size));
clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
cleared = 1;
}
@ -4887,7 +4894,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
if (REG_P (target))
emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
else
clear_storage (target, GEN_INT (size));
clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
cleared = 1;
}
@ -5132,7 +5139,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
if (REG_P (target))
emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
else
clear_storage (target, GEN_INT (size));
clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
cleared = 1;
}

View File

@ -367,7 +367,9 @@ enum block_op_methods
{
BLOCK_OP_NORMAL,
BLOCK_OP_NO_LIBCALL,
BLOCK_OP_CALL_PARM
BLOCK_OP_CALL_PARM,
/* Like BLOCK_OP_NORMAL, but the libcall can be tail call optimized. */
BLOCK_OP_TAILCALL
};
extern void init_block_move_fn (const char *);
@ -419,7 +421,7 @@ extern void use_group_regs (rtx *, rtx);
/* Write zeros through the storage of OBJECT.
If OBJECT has BLKmode, SIZE is its length in bytes. */
extern rtx clear_storage (rtx, rtx);
extern rtx clear_storage (rtx, rtx, enum block_op_methods);
/* Determine whether the LEN bytes can be moved by using several move
instructions. Return nonzero if a call to move_by_pieces should

View File

@ -1,5 +1,8 @@
2005-05-04 Jakub Jelinek <jakub@redhat.com>
PR middle-end/21265
* gcc.dg/20050503-1.c: New test.
PR rtl-optimization/21239
* gcc.dg/i386-sse-11.c: New test.

View File

@ -0,0 +1,49 @@
/* PR middle-end/21265
Test whether tail call information is propagated through builtin
expanders. */
/* { dg-do compile } */
/* { dg-options "-O2" } */
typedef __SIZE_TYPE__ size_t;
#define F(n, rettype, name, args, callargs) \
extern rettype name args; \
rettype test##n args \
{ \
return name callargs; \
}
#define F1(n, rettype, name, t1, callargs) \
F (n, rettype, name, (t1 a1), callargs)
#define F2(n, rettype, name, t1, t2, callargs) \
F (n, rettype, name, (t1 a1, t2 a2), callargs)
#define F3(n, rettype, name, t1, t2, t3, callargs) \
F (n, rettype, name, (t1 a1, t2 a2, t3 a3), callargs)
F3 (1a, void *, memcpy, void *, const void *, size_t, (a1, a2, a3))
F3 (1b, void *, memcpy, void *, const void *, size_t, (a1, a2, 10))
F3 (2a, void *, mempcpy, void *, const void *, size_t, (a1, a2, a3))
F3 (2b, void *, mempcpy, void *, const void *, size_t, (a1, a2, 10))
F3 (3a, void *, memmove, void *, const void *, size_t, (a1, a2, a3))
F3 (3b, void *, memmove, void *, const void *, size_t, (a1, "abcdefghijklmno", a3))
F3 (4a, void *, memset, void *, int, size_t, (a1, a2, a3))
F3 (4b, void *, memset, void *, int, size_t, (a1, a2, 156))
F3 (4c, void *, memset, void *, int, size_t, (a1, 0, a3))
F3 (4d, void *, memset, void *, int, size_t, (a1, 0, 10000))
F3 (5a, int, memcmp, const void *, const void *, size_t, (a1, a2, a3))
F3 (5b, int, memcmp, const void *, const void *, size_t, (a1, "abcdefghijkl", a3))
F2 (6, char *, strcpy, char *, const char *, (a1, a2))
F2 (7, char *, stpcpy, char *, const char *, (a1, a2))
F3 (8, char *, strncpy, char *, const char *, size_t, (a1, a2, a3))
F3 (9, char *, stpncpy, char *, const char *, size_t, (a1, a2, a3))
F2 (10, char *, strcat, char *, const char *, (a1, a2))
F3 (11, char *, strncat, char *, const char *, size_t, (a1, a2, a3))
F1 (12a, size_t, strlen, const char *, (a1))
F1 (12b, size_t, strlen, const char *, ("foobar"))
F2 (13a, int, strcmp, const char *, const char *, (a1, a2))
F2 (13b, int, strcmp, const char *, const char *, (a1, "abcdefghijklm"))
F3 (14a, int, strncmp, const char *, const char *, size_t, (a1, a2, a3))
F3 (14b, int, strncmp, const char *, const char *, size_t, (a1, "abcdefghijklm", 10))
F2 (15, char *, strchr, const char *, int, (a1, a2))
/* All the calls above should be tail call optimized on i?86/x86-64. */
/* { dg-final { scan-assembler-not "call" { target i?86-*-linux* x86_64-*-linux* } } } */