For an MS ABI va_list, ix86_gimplify_va_arg calls
std_gimplify_va_arg_expr, which asks the pass_by_reference hook whether
the argument was passed by reference. Without cumulative args, the hook
assumes the default ABI, SysV on Linux, so va_arg reads an argument
passed by reference as if it were passed by value.
Split the ABI-specific decision out of ix86_pass_by_reference. For an
indirect MS ABI argument, follow ia64_gimplify_va_arg: use the standard
va_arg helper to read its pointer and then dereference it. Use the
explicit SysV ABI for a SysV va_list so the opposite target-default
mismatch is handled as well.
gcc/ChangeLog:
* config/i386/i386.cc (ix86_pass_by_reference_abi): New function,
split out of ...
(ix86_pass_by_reference): ... this.
(ix86_gimplify_va_arg): Derive argument indirection from the ABI of
the va_list type. Read indirect MS ABI arguments as pointers.
gcc/testsuite/ChangeLog:
* gcc.target/i386/va-arg-abi-attribute.c: New test.
Signed-off-by: Oleg Tolmatcev <[email protected]>
---
gcc/config/i386/i386.cc | 37 +++++++---
.../gcc.target/i386/va-arg-abi-attribute.c | 73 +++++++++++++++++++
2 files changed, 101 insertions(+), 9 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/va-arg-abi-attribute.c
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index e9015d2427..3b1f680b22 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -3655,17 +3655,15 @@ ix86_function_arg (cumulative_args_t cum_v, const
function_arg_info &arg)
reference. If nonzero for an argument, a copy of that argument is
made in memory and a pointer to the argument is passed instead of
the argument itself. The pointer is passed in whatever way is
- appropriate for passing a pointer to that type. */
+ appropriate for passing a pointer to that type. CALL_ABI is the
+ calling convention the argument is passed under. */
static bool
-ix86_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg)
+ix86_pass_by_reference_abi (enum calling_abi call_abi,
+ const function_arg_info &arg)
{
- CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
-
if (TARGET_64BIT)
{
- enum calling_abi call_abi = cum ? cum->call_abi : ix86_abi;
-
/* See Windows x64 Software Convention. */
if (call_abi == MS_ABI)
{
@@ -3695,6 +3693,16 @@ ix86_pass_by_reference (cumulative_args_t cum_v, const
function_arg_info &arg)
return false;
}
+/* Implement TARGET_PASS_BY_REFERENCE. */
+
+static bool
+ix86_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
+ return ix86_pass_by_reference_abi (cum ? cum->call_abi : ix86_abi, arg);
+}
+
/* Return true when TYPE should be 128bit aligned for 32bit argument
passing ABI. XXX: This function is obsolete and is only used for
checking psABI compatibility with previous versions of GCC. */
@@ -4988,7 +4996,7 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq
*pre_p,
tree lab_false, lab_over = NULL_TREE;
tree addr, t2;
rtx container;
- int indirect_p = 0;
+ bool indirect_p;
tree ptrtype;
machine_mode nat_mode;
unsigned int arg_boundary;
@@ -4996,7 +5004,17 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq
*pre_p,
/* Only 64bit target needs something special. */
if (is_va_list_char_pointer (TREE_TYPE (valist)))
- return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
+ {
+ indirect_p
+ = ix86_pass_by_reference_abi (MS_ABI, function_arg_info (type, false));
+ if (indirect_p)
+ {
+ type = build_pointer_type (type);
+ t = std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
+ return build_va_arg_indirect_ref (t);
+ }
+ return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
+ }
f_gpr = TYPE_FIELDS (TREE_TYPE (sysv_va_list_type_node));
f_fpr = DECL_CHAIN (f_gpr);
@@ -5010,7 +5028,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq
*pre_p,
ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
- indirect_p = pass_va_arg_by_reference (type);
+ indirect_p
+ = ix86_pass_by_reference_abi (SYSV_ABI, function_arg_info (type, false));
if (indirect_p)
type = build_pointer_type (type);
size = arg_int_size_in_bytes (type);
diff --git a/gcc/testsuite/gcc.target/i386/va-arg-abi-attribute.c
b/gcc/testsuite/gcc.target/i386/va-arg-abi-attribute.c
new file mode 100644
index 0000000000..48aed0c914
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/va-arg-abi-attribute.c
@@ -0,0 +1,73 @@
+/* ix86_gimplify_va_arg must decide whether an argument is passed by
+ reference from the va_list type, not from the ABI of the function
+ containing the va_arg or from the target default. */
+/* { dg-do run { target { lp64 || llp64 } } } */
+/* { dg-options "-std=gnu99" } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+struct s3 { char a, b, c; };
+struct s8 { int a, b; };
+struct s12 { int a, b, c; };
+struct s16 { long long a, b; };
+
+#define READER(name, abi, list)
\
+static __attribute__ ((abi, noipa)) void \
+name (list ap) \
+{ \
+ struct s3 x3 = __builtin_va_arg (ap, struct s3); \
+ struct s8 x8 = __builtin_va_arg (ap, struct s8); \
+ struct s12 x12 = __builtin_va_arg (ap, struct s12); \
+ struct s16 x16 = __builtin_va_arg (ap, struct s16); \
+ v4si v = __builtin_va_arg (ap, v4si);
\
+ int i = __builtin_va_arg (ap, int); \
+ if (x3.a != 1 || x3.b != 2 || x3.c != 3 \
+ || x8.a != 4 || x8.b != 5
\
+ || x12.a != 6 || x12.b != 7 || x12.c != 8
\
+ || x16.a != 9 || x16.b != 10 \
+ || v[0] != 11 || v[1] != 12 || v[2] != 13 || v[3] != 14 \
+ || i != 15) \
+ __builtin_abort ();
\
+}
+
+READER (sysv_reads_sysv, sysv_abi, __builtin_sysv_va_list)
+READER (ms_reads_sysv, ms_abi, __builtin_sysv_va_list)
+READER (sysv_reads_ms, sysv_abi, __builtin_ms_va_list)
+READER (ms_reads_ms, ms_abi, __builtin_ms_va_list)
+
+static __attribute__ ((sysv_abi, noipa)) void
+sysv_varargs (int n, ...)
+{
+ __builtin_sysv_va_list ap;
+ __builtin_sysv_va_start (ap, n);
+ sysv_reads_sysv (ap);
+ __builtin_sysv_va_end (ap);
+ __builtin_sysv_va_start (ap, n);
+ ms_reads_sysv (ap);
+ __builtin_sysv_va_end (ap);
+}
+
+static __attribute__ ((ms_abi, noipa)) void
+ms_varargs (int n, ...)
+{
+ __builtin_ms_va_list ap;
+ __builtin_ms_va_start (ap, n);
+ sysv_reads_ms (ap);
+ __builtin_ms_va_end (ap);
+ __builtin_ms_va_start (ap, n);
+ ms_reads_ms (ap);
+ __builtin_ms_va_end (ap);
+}
+
+int
+main (void)
+{
+ struct s3 x3 = { 1, 2, 3 };
+ struct s8 x8 = { 4, 5 };
+ struct s12 x12 = { 6, 7, 8 };
+ struct s16 x16 = { 9, 10 };
+ v4si v = { 11, 12, 13, 14 };
+
+ sysv_varargs (0, x3, x8, x12, x16, v, 15);
+ ms_varargs (0, x3, x8, x12, x16, v, 15);
+ return 0;
+}
--
2.56.0.windows.1