diff mbox series

[v2] Add a target calls hook: TARGET_PUSH_ARGUMENT

Message ID 20210527201857.527093-1-hjl.tools@gmail.com
State New
Headers show
Series [v2] Add a target calls hook: TARGET_PUSH_ARGUMENT | expand

Commit Message

H.J. Lu May 27, 2021, 8:18 p.m. UTC
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):
	(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.
---
 gcc/calls.c                                |  6 +++---
 gcc/config/bpf/bpf.h                       |  3 ---
 gcc/config/cr16/cr16.c                     |  2 ++
 gcc/config/cr16/cr16.h                     |  2 --
 gcc/config/i386/i386.c                     | 14 +++++++++++++
 gcc/config/i386/i386.h                     |  7 +------
 gcc/config/m32c/m32c.c                     |  3 +++
 gcc/config/m32c/m32c.h                     |  1 -
 gcc/config/nios2/nios2.h                   |  1 -
 gcc/config/pru/pru.h                       |  1 -
 gcc/defaults.h                             | 11 +---------
 gcc/doc/tm.texi                            | 19 +++++++++--------
 gcc/doc/tm.texi.in                         |  9 +-------
 gcc/expr.c                                 | 14 ++++++++++---
 gcc/hooks.c                                |  8 ++++++++
 gcc/hooks.h                                |  1 +
 gcc/rtlanal.c                              |  2 +-
 gcc/target.def                             | 14 +++++++++++++
 gcc/targhooks.c                            | 12 +++++++++++
 gcc/targhooks.h                            |  1 +
 gcc/testsuite/gcc.target/i386/pr100704-1.c | 24 ++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr100704-2.c | 23 +++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr100704-3.c | 20 ++++++++++++++++++
 23 files changed, 151 insertions(+), 47 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr100704-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr100704-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr100704-3.c

Comments

Jeff Law June 14, 2021, 2:21 a.m. UTC | #1
On 5/27/2021 2:18 PM, H.J. Lu via Gcc-patches wrote:
> 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):
> 	(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.
OK.
jeff
diff mbox series

Patch

diff --git a/gcc/calls.c b/gcc/calls.c
index f3da1839dc5..336b05f01c4 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -3727,7 +3727,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;
@@ -3836,7 +3836,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.  */
@@ -5477,7 +5477,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);
     }
 
diff --git a/gcc/config/bpf/bpf.h b/gcc/config/bpf/bpf.h
index 4c5b19e262b..80195cea5b2 100644
--- a/gcc/config/bpf/bpf.h
+++ b/gcc/config/bpf/bpf.h
@@ -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
diff --git a/gcc/config/cr16/cr16.c b/gcc/config/cr16/cr16.c
index 6c81c399f70..aaa2260fc85 100644
--- a/gcc/config/cr16/cr16.c
+++ b/gcc/config/cr16/cr16.c
@@ -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
diff --git a/gcc/config/cr16/cr16.h b/gcc/config/cr16/cr16.h
index 4ce9e81b0e3..a3ad0359f7d 100644
--- a/gcc/config/cr16/cr16.h
+++ b/gcc/config/cr16/cr16.h
@@ -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
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index f3b451835da..f47353ea057 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -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.  */
 
@@ -23637,6 +23649,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
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 53d503fc6e0..b286a655c56 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -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.  */
diff --git a/gcc/config/m32c/m32c.c b/gcc/config/m32c/m32c.c
index b1cb3591da6..d22bdd79c71 100644
--- a/gcc/config/m32c/m32c.c
+++ b/gcc/config/m32c/m32c.c
@@ -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
diff --git a/gcc/config/m32c/m32c.h b/gcc/config/m32c/m32c.h
index 635f5924c20..228a73d1c42 100644
--- a/gcc/config/m32c/m32c.h
+++ b/gcc/config/m32c/m32c.h
@@ -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
 
diff --git a/gcc/config/nios2/nios2.h b/gcc/config/nios2/nios2.h
index 1840a466f96..dfca12cc525 100644
--- a/gcc/config/nios2/nios2.h
+++ b/gcc/config/nios2/nios2.h
@@ -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.  */
diff --git a/gcc/config/pru/pru.h b/gcc/config/pru/pru.h
index 4c35a7d7ee3..9b6be323e6d 100644
--- a/gcc/config/pru/pru.h
+++ b/gcc/config/pru/pru.h
@@ -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.  */
diff --git a/gcc/defaults.h b/gcc/defaults.h
index 91216593e75..ba79a8e48ed 100644
--- a/gcc/defaults.h
+++ b/gcc/defaults.h
@@ -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
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index e3a080e4a7c..6e634f24c38 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -3807,14 +3807,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
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index d9fbbe20e6f..88aa110dcdb 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -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
diff --git a/gcc/expr.c b/gcc/expr.c
index e4660f0e90a..05b0ac1df03 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -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
diff --git a/gcc/hooks.c b/gcc/hooks.c
index 680271f76a4..4f14abff206 100644
--- a/gcc/hooks.c
+++ b/gcc/hooks.c
@@ -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.  */
 
diff --git a/gcc/hooks.h b/gcc/hooks.h
index add9a742e41..71781c790a1 100644
--- a/gcc/hooks.h
+++ b/gcc/hooks.h
@@ -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);
diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c
index 712c2c28578..55c338e7eac 100644
--- a/gcc/rtlanal.c
+++ b/gcc/rtlanal.c
@@ -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);
diff --git a/gcc/target.def b/gcc/target.def
index 1dffedc81e4..27e8c30f3a5 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4749,6 +4749,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\
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 08f676b31f6..44a1facedcf 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -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 &)
 {
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index b537038c0aa..f70a307d26c 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -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 &);
diff --git a/gcc/testsuite/gcc.target/i386/pr100704-1.c b/gcc/testsuite/gcc.target/i386/pr100704-1.c
new file mode 100644
index 00000000000..02461db9695
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr100704-1.c
@@ -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 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr100704-2.c b/gcc/testsuite/gcc.target/i386/pr100704-2.c
new file mode 100644
index 00000000000..07b9bd18c7a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr100704-2.c
@@ -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 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr100704-3.c b/gcc/testsuite/gcc.target/i386/pr100704-3.c
new file mode 100644
index 00000000000..65f9745a197
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr100704-3.c
@@ -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\+" } } */