[v2,1/2] i386: Take the va_arg by-reference decision from the va_list type

Message ID 20260930224523.495-2-oleg.tolmatcev@gmail.com
State Committed
Commit 574921e7ac2058d3baed5f193b33280d072cf39d
Headers
Series [v2,1/2] i386: Take the va_arg by-reference decision from the va_list type |

Commit Message

Oleg Tolmatcev Sept. 30, 2026, 10:45 p.m. UTC
  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 <oleg.tolmatcev@gmail.com>
---
 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
  

Comments

Uros Bizjak Oct. 1, 2026, 6:43 a.m. UTC | #1
On Thu, Oct 1, 2026 at 12:46 AM Oleg Tolmatcev <oleg.tolmatcev@gmail.com> wrote:
>
> 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.

OK.

For future contributions, please state how you tested the patch, see
[1]. Also, if you don't have write access, please point out lack of
write access in your initial submission, as also advised in [1].

[1] https://gcc.gnu.org/contribute.html#patches

Thanks,
Uros.

>
> Signed-off-by: Oleg Tolmatcev <oleg.tolmatcev@gmail.com>
> ---
>  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
>
  
Jonathan Yong Oct. 5, 2026, 4:28 p.m. UTC | #2
On 10/1/26 06:43, Uros Bizjak wrote:
> On Thu, Oct 1, 2026 at 12:46 AM Oleg Tolmatcev <oleg.tolmatcev@gmail.com> wrote:
>>
>> 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.
> 
> OK.
> 
> For future contributions, please state how you tested the patch, see
> [1]. Also, if you don't have write access, please point out lack of
> write access in your initial submission, as also advised in [1].
> 
> [1] https://gcc.gnu.org/contribute.html#patches
> 
> Thanks,
> Uros.
> 
Can these be pushed or are there any further review? I also don't see 
the v2 second patch anywhere.
  
Uros Bizjak Oct. 5, 2026, 9:32 p.m. UTC | #3
On Mon, Oct 5, 2026 at 6:29 PM Jonathan Yong <10walls@gmail.com> wrote:
>
> On 10/1/26 06:43, Uros Bizjak wrote:
> > On Thu, Oct 1, 2026 at 12:46 AM Oleg Tolmatcev <oleg.tolmatcev@gmail.com> wrote:
> >>
> >> 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.
> >
> > OK.
> >
> > For future contributions, please state how you tested the patch, see
> > [1]. Also, if you don't have write access, please point out lack of
> > write access in your initial submission, as also advised in [1].
> >
> > [1] https://gcc.gnu.org/contribute.html#patches
> >
> > Thanks,
> > Uros.
> >
> Can these be pushed or are there any further review? I also don't see
> the v2 second patch anywhere.

Please go ahead and push this patch and [1] (approved v1 2/2).

[1] https://gcc.gnu.org/pipermail/gcc-patches/2026-September/733116.html

Thanks,
Uros.
  
Jonathan Yong Oct. 6, 2026, 10:52 a.m. UTC | #4
On 10/5/26 21:32, Uros Bizjak wrote:
>>>>
>>>> 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.
>>>
>>> OK.
>>>
>>> For future contributions, please state how you tested the patch, see
>>> [1]. Also, if you don't have write access, please point out lack of
>>> write access in your initial submission, as also advised in [1].
>>>
>>> [1] https://gcc.gnu.org/contribute.html#patches
>>>
>>> Thanks,
>>> Uros.
>>>
>> Can these be pushed or are there any further review? I also don't see
>> the v2 second patch anywhere.
> 
> Please go ahead and push this patch and [1] (approved v1 2/2).
> 
> [1] https://gcc.gnu.org/pipermail/gcc-patches/2026-September/733116.html
> 
> Thanks,
> Uros.

Thanks, pushed both to master branch.
  

Patch

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;
+}