Add a target calls hook: TARGET_PUSH_ARGUMENT

1. Replace PUSH_ARGS with a target calls hook, TARGET_PUSH_ARGUMENT, which
takes an integer argument.  When it returns true, push instructions will
be used to pass outgoing arguments.  If the argument is nonzero, it is
the number of bytes to push and indicates the PUSH instruction usage is
optional so that the backend can decide if PUSH instructions should be
generated.  Otherwise, the argument is zero.
2. Implement x86 target hook which returns false when the number of bytes
to push is no less than 16 (8 for 32-bit targets) if vector load and store
can be used.
3. Remove target PUSH_ARGS definitions which return 0 as it is the same
as the default.
4. Define TARGET_PUSH_ARGUMENT of cr16 and m32c to always return true.

gcc/

	PR target/100704
	* calls.c (expand_call): Replace PUSH_ARGS with
	targetm.calls.push_argument (0).
	(emit_library_call_value_1): Likewise.
	* defaults.h (PUSH_ARGS): Removed.
	(PUSH_ARGS_REVERSED): Replace PUSH_ARGS with
	targetm.calls.push_argument (0).
	* expr.c (block_move_libcall_safe_for_call_parm): Likewise.
	(emit_push_insn): Pass the number bytes to push to
	targetm.calls.push_argument and pass 0 if ARGS_ADDR is 0.
	* hooks.c (hook_bool_uint_true): New.
	* hooks.h (hook_bool_uint_true): Likewise.
	* rtlanal.c (nonzero_bits1): Replace PUSH_ARGS with
	targetm.calls.push_argument (0).
	* target.def (push_argument): Add a targetm.calls hook.
	* targhooks.c (default_push_argument): New.
	* targhooks.h (default_push_argument): Likewise.
	* config/bpf/bpf.h (PUSH_ARGS): Removed.
	* config/cr16/cr16.c (TARGET_PUSH_ARGUMENT): New.
	* config/cr16/cr16.h (PUSH_ARGS): Removed.
	* config/i386/i386.c (ix86_push_argument): New.
	(TARGET_PUSH_ARGUMENT): Likewise.
	* config/i386/i386.h (PUSH_ARGS): Removed.
	* config/m32c/m32c.c (TARGET_PUSH_ARGUMENT): New.
	* config/m32c/m32c.h (PUSH_ARGS): Removed.
	* config/nios2/nios2.h (PUSH_ARGS): Likewise.
	* config/pru/pru.h (PUSH_ARGS): Likewise.
	* doc/tm.texi.in: Remove PUSH_ARGS documentation.  Add
	TARGET_PUSH_ARGUMENT hook.
	* doc/tm.texi: Regenerated.

gcc/testsuite/

	PR target/100704
	* gcc.target/i386/pr100704-1.c: New test.
	* gcc.target/i386/pr100704-2.c: Likewise.
	* gcc.target/i386/pr100704-3.c: Likewise.
This commit is contained in:
H.J. Lu 2021-05-21 11:56:55 -07:00
parent 20a2c8ace0
commit 967b465302
23 changed files with 151 additions and 47 deletions

View File

@ -3729,7 +3729,7 @@ expand_call (tree exp, rtx target, int ignore)
So the entire argument block must then be preallocated (i.e., we
ignore PUSH_ROUNDING in that case). */
int must_preallocate = !PUSH_ARGS;
int must_preallocate = !targetm.calls.push_argument (0);
/* Size of the stack reserved for parameter registers. */
int reg_parm_stack_space = 0;
@ -3838,7 +3838,7 @@ expand_call (tree exp, rtx target, int ignore)
#endif
if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl)))
&& reg_parm_stack_space > 0 && PUSH_ARGS)
&& reg_parm_stack_space > 0 && targetm.calls.push_argument (0))
must_preallocate = 1;
/* Set up a place to return a structure. */
@ -5479,7 +5479,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
}
else
{
if (!PUSH_ARGS)
if (!targetm.calls.push_argument (0))
argblock = push_block (gen_int_mode (args_size.constant, Pmode), 0, 0);
}

View File

@ -288,9 +288,6 @@ enum reg_class
never used when passing arguments. However, we still have to
define the constants below. */
/* If nonzero, push insns will be used to pass outgoing arguments. */
#define PUSH_ARGS 0
/* If nonzero, function arguments will be evaluated from last to
first, rather than from first to last. */
#define PUSH_ARGS_REVERSED 1

View File

@ -158,6 +158,8 @@ static void cr16_print_operand_address (FILE *, machine_mode, rtx);
#define TARGET_CLASS_LIKELY_SPILLED_P cr16_class_likely_spilled_p
/* Passing function arguments. */
#undef TARGET_PUSH_ARGUMENT
#define TARGET_PUSH_ARGUMENT hook_bool_uint_true
#undef TARGET_FUNCTION_ARG
#define TARGET_FUNCTION_ARG cr16_function_arg
#undef TARGET_FUNCTION_ARG_ADVANCE

View File

@ -376,8 +376,6 @@ enum reg_class
#define ACCUMULATE_OUTGOING_ARGS 0
#define PUSH_ARGS 1
#define PUSH_ROUNDING(BYTES) cr16_push_rounding (BYTES)
#ifndef CUMULATIVE_ARGS

View File

@ -4191,6 +4191,18 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
}
}
/* Implement TARGET_PUSH_ARGUMENT. */
static bool
ix86_push_argument (unsigned int npush)
{
/* If SSE2 is available, use vector move to put large argument onto
stack. NB: In 32-bit mode, use 8-byte vector move. */
return ((!TARGET_SSE2 || npush < (TARGET_64BIT ? 16 : 8))
&& TARGET_PUSH_ARGS
&& !ACCUMULATE_OUTGOING_ARGS);
}
/* Create the va_list data type. */
@ -23695,6 +23707,8 @@ ix86_run_selftests (void)
#define TARGET_C_EXCESS_PRECISION ix86_get_excess_precision
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
#undef TARGET_PUSH_ARGUMENT
#define TARGET_PUSH_ARGUMENT ix86_push_argument
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS ix86_setup_incoming_varargs
#undef TARGET_MUST_PASS_IN_STACK

View File

@ -1462,13 +1462,8 @@ enum reg_class
|| TARGET_64BIT_MS_ABI \
|| (TARGET_MACHO && crtl->profile))
/* If defined, a C expression whose value is nonzero when we want to use PUSH
instructions to pass outgoing arguments. */
#define PUSH_ARGS (TARGET_PUSH_ARGS && !ACCUMULATE_OUTGOING_ARGS)
/* We want the stack and args grow in opposite directions, even if
PUSH_ARGS is 0. */
targetm.calls.push_argument returns false. */
#define PUSH_ARGS_REVERSED 1
/* Offset of first parameter from the argument pointer register value. */

View File

@ -1296,6 +1296,9 @@ m32c_push_rounding (poly_int64 n)
return (n + 1) & ~1;
}
#undef TARGET_PUSH_ARGUMENT
#define TARGET_PUSH_ARGUMENT hook_bool_uint_true
/* Passing Arguments in Registers */
/* Implements TARGET_FUNCTION_ARG. Arguments are passed partly in

View File

@ -472,7 +472,6 @@ enum reg_class
/* Passing Function Arguments on the Stack */
#define PUSH_ARGS 1
#define PUSH_ROUNDING(N) m32c_push_rounding (N)
#define CALL_POPS_ARGS(C) 0

View File

@ -297,7 +297,6 @@ typedef struct nios2_args
((REGNO) >= FIRST_ARG_REGNO && (REGNO) <= LAST_ARG_REGNO)
/* Passing function arguments on stack. */
#define PUSH_ARGS 0
#define ACCUMULATE_OUTGOING_ARGS 1
/* We define TARGET_RETURN_IN_MEMORY, so set to zero. */

View File

@ -339,7 +339,6 @@ typedef struct pru_args
((REGNO) >= FIRST_ARG_REGNUM && (REGNO) <= LAST_ARG_REGNUM)
/* Passing function arguments on stack. */
#define PUSH_ARGS 0
#define ACCUMULATE_OUTGOING_ARGS 1
/* We define TARGET_RETURN_IN_MEMORY, so set to zero. */

View File

@ -801,15 +801,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#define NEXT_OBJC_RUNTIME 0
#endif
/* Supply a default definition for PUSH_ARGS. */
#ifndef PUSH_ARGS
#ifdef PUSH_ROUNDING
#define PUSH_ARGS !ACCUMULATE_OUTGOING_ARGS
#else
#define PUSH_ARGS 0
#endif
#endif
/* Decide whether a function's arguments should be processed
from first to last or from last to first.
@ -820,7 +811,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#ifndef PUSH_ARGS_REVERSED
#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
#define PUSH_ARGS_REVERSED PUSH_ARGS
#define PUSH_ARGS_REVERSED targetm.calls.push_argument (0)
#endif
#endif

View File

@ -3900,14 +3900,17 @@ cases of mismatch, it also makes for better code on certain machines.
The default is to not promote prototypes.
@end deftypefn
@defmac PUSH_ARGS
A C expression. If nonzero, push insns will be used to pass
outgoing arguments.
If the target machine does not have a push instruction, set it to zero.
That directs GCC to use an alternate strategy: to
allocate the entire argument block and then store the arguments into
it. When @code{PUSH_ARGS} is nonzero, @code{PUSH_ROUNDING} must be defined too.
@end defmac
@deftypefn {Target Hook} bool TARGET_PUSH_ARGUMENT (unsigned int @var{npush})
This target hook returns @code{true} if push instructions will be
used to pass outgoing arguments. When the push instruction usage is
optional, @var{npush} is nonzero to indicate the number of bytes to
push. Otherwise, @var{npush} is zero. If the target machine does not
have a push instruction or push instruction should be avoided,
@code{false} should be returned. That directs GCC to use an alternate
strategy: to allocate the entire argument block and then store the
arguments into it. If this target hook may return @code{true},
@code{PUSH_ROUNDING} must be defined.
@end deftypefn
@defmac PUSH_ARGS_REVERSED
A C expression. If nonzero, function arguments will be evaluated from

View File

@ -3100,14 +3100,7 @@ control passing certain arguments in registers.
@hook TARGET_PROMOTE_PROTOTYPES
@defmac PUSH_ARGS
A C expression. If nonzero, push insns will be used to pass
outgoing arguments.
If the target machine does not have a push instruction, set it to zero.
That directs GCC to use an alternate strategy: to
allocate the entire argument block and then store the arguments into
it. When @code{PUSH_ARGS} is nonzero, @code{PUSH_ROUNDING} must be defined too.
@end defmac
@hook TARGET_PUSH_ARGUMENT
@defmac PUSH_ARGS_REVERSED
A C expression. If nonzero, function arguments will be evaluated from

View File

@ -1823,7 +1823,7 @@ block_move_libcall_safe_for_call_parm (void)
tree fn;
/* If arguments are pushed on the stack, then they're safe. */
if (PUSH_ARGS)
if (targetm.calls.push_argument (0))
return true;
/* If registers go on the stack anyway, any argument is sure to clobber
@ -4639,11 +4639,19 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
skip = (reg_parm_stack_space == 0) ? 0 : used;
#ifdef PUSH_ROUNDING
/* NB: Let the backend known the number of bytes to push and
decide if push insns should be generated. */
unsigned int push_size;
if (CONST_INT_P (size))
push_size = INTVAL (size);
else
push_size = 0;
/* Do it with several push insns if that doesn't take lots of insns
and if there is no difficulty with push insns that skip bytes
on the stack for alignment purposes. */
if (args_addr == 0
&& PUSH_ARGS
&& targetm.calls.push_argument (push_size)
&& CONST_INT_P (size)
&& skip == 0
&& MEM_ALIGN (xinner) >= align
@ -4848,7 +4856,7 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
anti_adjust_stack (gen_int_mode (extra, Pmode));
#ifdef PUSH_ROUNDING
if (args_addr == 0 && PUSH_ARGS)
if (args_addr == 0 && targetm.calls.push_argument (0))
emit_single_push_insn (mode, x, type);
else
#endif

View File

@ -520,6 +520,14 @@ hook_void_gcc_optionsp (struct gcc_options *)
{
}
/* Generic hook that takes an unsigned int and returns true. */
bool
hook_bool_uint_true (unsigned int)
{
return true;
}
/* Generic hook that takes an unsigned int, an unsigned int pointer and
returns false. */

View File

@ -89,6 +89,7 @@ extern void hook_void_tree (tree);
extern void hook_void_tree_treeptr (tree, tree *);
extern void hook_void_int_int (int, int);
extern void hook_void_gcc_optionsp (struct gcc_options *);
extern bool hook_bool_uint_true (unsigned int);
extern bool hook_bool_uint_uintp_false (unsigned int, unsigned int *);
extern int hook_int_uint_mode_1 (unsigned int, machine_mode);

View File

@ -4870,7 +4870,7 @@ nonzero_bits1 (const_rtx x, scalar_int_mode mode, const_rtx known_x,
/* If PUSH_ROUNDING is defined, it is possible for the
stack to be momentarily aligned only to that amount,
so we pick the least alignment. */
if (x == stack_pointer_rtx && PUSH_ARGS)
if (x == stack_pointer_rtx && targetm.calls.push_argument (0))
{
poly_uint64 rounded_1 = PUSH_ROUNDING (poly_int64 (1));
alignment = MIN (known_alignment (rounded_1), alignment);

View File

@ -4752,6 +4752,20 @@ Most ports do not need to implement anything for this hook.",
void, (void),
hook_void_void)
DEFHOOK
(push_argument,
"This target hook returns @code{true} if push instructions will be\n\
used to pass outgoing arguments. When the push instruction usage is\n\
optional, @var{npush} is nonzero to indicate the number of bytes to\n\
push. Otherwise, @var{npush} is zero. If the target machine does not\n\
have a push instruction or push instruction should be avoided,\n\
@code{false} should be returned. That directs GCC to use an alternate\n\
strategy: to allocate the entire argument block and then store the\n\
arguments into it. If this target hook may return @code{true},\n\
@code{PUSH_ROUNDING} must be defined.",
bool, (unsigned int npush),
default_push_argument)
DEFHOOK
(strict_argument_naming,
"Define this hook to return @code{true} if the location where a function\n\

View File

@ -770,6 +770,18 @@ hook_void_CUMULATIVE_ARGS_tree (cumulative_args_t ca ATTRIBUTE_UNUSED,
{
}
/* Default implementation of TARGET_PUSH_ARGUMENT. */
bool
default_push_argument (unsigned int)
{
#ifdef PUSH_ROUNDING
return !ACCUMULATE_OUTGOING_ARGS;
#else
return false;
#endif
}
void
default_function_arg_advance (cumulative_args_t, const function_arg_info &)
{

View File

@ -149,6 +149,7 @@ extern const char *hook_invalid_arg_for_unprototyped_fn
(const_tree, const_tree, const_tree);
extern void default_function_arg_advance
(cumulative_args_t, const function_arg_info &);
extern bool default_push_argument (unsigned int);
extern HOST_WIDE_INT default_function_arg_offset (machine_mode, const_tree);
extern pad_direction default_function_arg_padding (machine_mode, const_tree);
extern rtx default_function_arg (cumulative_args_t, const function_arg_info &);

View File

@ -0,0 +1,24 @@
/* { dg-do compile { target { ! ia32 } } } */
/* { dg-options "-O2 -march=x86-64" } */
struct S
{
long long s1 __attribute__ ((aligned (8)));
unsigned s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14;
};
extern struct S a[];
void bar (struct S);
void
foo (void)
{
bar (a[0]);
}
/* { dg-final { scan-assembler-not "pushq" } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, \\(%\[\^,\]+\\)" 1 } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, 16\\(%\[\^,\]+\\)" 1 } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, 32\\(%\[\^,\]+\\)" 1 } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, 48\\(%\[\^,\]+\\)" 1 } } */

View File

@ -0,0 +1,23 @@
/* { dg-do compile { target { ! ia32 } } } */
/* { dg-options "-O2 -march=x86-64" } */
struct S
{
char array[64];
};
extern struct S a[];
void bar (struct S);
void
foo (void)
{
bar (a[0]);
}
/* { dg-final { scan-assembler-not "pushq" } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, \\(%\[\^,\]+\\)" 1 } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, 16\\(%\[\^,\]+\\)" 1 } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, 32\\(%\[\^,\]+\\)" 1 } } */
/* { dg-final { scan-assembler-times "movups\[\\t \]%xmm\[0-9\]+, 48\\(%\[\^,\]+\\)" 1 } } */

View File

@ -0,0 +1,20 @@
/* { dg-do compile } */
/* { dg-options "-O2 -mno-sse" } */
struct S
{
long long s1 __attribute__ ((aligned (8)));
unsigned s2, s3;
};
extern struct S foooo[];
void bar (int, int, int, int, int, int, struct S);
void
foo (void)
{
bar (1, 2, 3, 4, 5, 6, foooo[0]);
}
/* { dg-final { scan-assembler "push\[lq\]\tfoooo\+" } } */