aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2021-05-21 11:56:55 -0700
committerH.J. Lu <hjl.tools@gmail.com>2021-06-17 06:33:14 -0700
commit967b46530234b4e6ad3983057705aea6c20a03c4 (patch)
tree87a0ce8f7eec0c4ae72c1ee49f30fe53aec84268 /gcc
parent20a2c8ace0ab56c147fd995432abd5e7cf89b0e3 (diff)
downloadgcc-967b46530234b4e6ad3983057705aea6c20a03c4.zip
gcc-967b46530234b4e6ad3983057705aea6c20a03c4.tar.gz
gcc-967b46530234b4e6ad3983057705aea6c20a03c4.tar.bz2
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/calls.c6
-rw-r--r--gcc/config/bpf/bpf.h3
-rw-r--r--gcc/config/cr16/cr16.c2
-rw-r--r--gcc/config/cr16/cr16.h2
-rw-r--r--gcc/config/i386/i386.c14
-rw-r--r--gcc/config/i386/i386.h7
-rw-r--r--gcc/config/m32c/m32c.c3
-rw-r--r--gcc/config/m32c/m32c.h1
-rw-r--r--gcc/config/nios2/nios2.h1
-rw-r--r--gcc/config/pru/pru.h1
-rw-r--r--gcc/defaults.h11
-rw-r--r--gcc/doc/tm.texi19
-rw-r--r--gcc/doc/tm.texi.in9
-rw-r--r--gcc/expr.c14
-rw-r--r--gcc/hooks.c8
-rw-r--r--gcc/hooks.h1
-rw-r--r--gcc/rtlanal.c2
-rw-r--r--gcc/target.def14
-rw-r--r--gcc/targhooks.c12
-rw-r--r--gcc/targhooks.h1
-rw-r--r--gcc/testsuite/gcc.target/i386/pr100704-1.c24
-rw-r--r--gcc/testsuite/gcc.target/i386/pr100704-2.c23
-rw-r--r--gcc/testsuite/gcc.target/i386/pr100704-3.c20
23 files changed, 151 insertions, 47 deletions
diff --git a/gcc/calls.c b/gcc/calls.c
index a7c78ed..4bf2b5d 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -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);
}
diff --git a/gcc/config/bpf/bpf.h b/gcc/config/bpf/bpf.h
index 4c5b19e..80195ce 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 6c81c39..aaa2260 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 4ce9e81..a3ad035 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 a612558..7d0d414 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. */
@@ -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
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 182b327..6e0340a 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 b1cb359..d22bdd7 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 635f592..228a73d 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 1840a46..dfca12c 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 4c35a7d..9b6be32 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 9121659..ba79a8e 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 fc7eb77..2a41ae5 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -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
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 33532f0..f881cda 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 231487f..025033c 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 680271f..4f14abf 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 add9a74..71781c7 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 712c2c28..55c338e 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 ebe5803..c009671 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4753,6 +4753,20 @@ Most ports do not need to implement anything for this hook.",
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\
argument is passed depends on whether or not it is a named argument.\n\
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 08f676b..44a1fac 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 b537038..f70a307d 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 0000000..02461db
--- /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 0000000..07b9bd1
--- /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 0000000..65f9745
--- /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\+" } } */