https://gcc.gnu.org/g:574921e7ac2058d3baed5f193b33280d072cf39d
commit r17-4926-g574921e7ac2058d3baed5f193b33280d072cf39d Author: Oleg Tolmatcev <[email protected]> Date: Thu Oct 1 00:45:24 2026 +0200 i386: Take the va_arg by-reference decision from the va_list type 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]> Signed-off-by: Jonathan Yong <[email protected]> Diff: --- gcc/config/i386/i386.cc | 37 ++++++++--- .../gcc.target/i386/va-arg-abi-attribute.c | 73 ++++++++++++++++++++++ 2 files changed, 101 insertions(+), 9 deletions(-) diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index afe799b87dc1..527be92c7310 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 000000000000..48aed0c914c5 --- /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; +}
