From: Saurabh Jha <[email protected]>
The aarch64-w64-mingw32 target is different from aarch64-**-linux-gnu
targets with respect to how arguments for variadic functions are
handled. Specifically:
1. Homogeneous Floating-Point Aggregate (HFA) and Homogeneous Vector
Aggregate (HVA) are not handled in a special way. They are handled
like other composite types.
2. SIMD and Floating-Point registers aren't used.
This patch implements these differences for the aarch64-w64-mingw32
target. The new ABI specific functions to be used in target hooks are
declared in aarch64-abi-ms-protos.h and defined aarch64-abi-ms.cc. We
identify whether we are on aarch64-w64-mingw32 by the
TARGET_AARCH64_MS_ABI macro.
gcc/ChangeLog:
* config.gcc: Add new Makefile fragment and new object file.
* config/aarch64/aarch64-builtins.cc
(aarch64_ms_variadic_abi_init_builtins): Initialize builtin
variadic functions for aarch64-w64-mingw32.
* config/aarch64/aarch64-protos.h
(aarch64_ms_variadic_abi_init_builtins): Initialize builtin
variadic functions for aarch64-w64-mingw32.
* config/aarch64/aarch64.cc
(handle_aarch64_vector_pcs_attribute): Add support for
ARM_PCS_MS_VARIADIC.
(aarch64_ms_variadic_abi): Return descriptor to variadic
function call ABI for aarch64-w64-mingw32 target.
(aarch64_fntype_abi): Add support for variadic functions for
aarch64-w64-mingw32 target.
(aarch64_reg_save_mode): Add support for ARM_PCS_MS_VARIADIC.
(num_pcs_arg_regs): Add support for ARM_PCS_MS_VARIADIC.
(get_pcs_arg_reg): Add support for ARM_PCS_MS_VARIADIC.
(aarch64_arg_size): Returns size of argument.
(aarch64_ms_variadic_abi_layout_arg): aarch64-w64-mingw32
specific support for variadic ABI.
(aarch64_layout_arg): Add support for ARM_PCS_MS_VARIADIC.
(aarch64_function_arg): Implement TARGET_FUNCTION_ARG.
(aarch64_function_arg_advance): Add support for
ARM_PCS_MS_VARIADIC.
(aarch64_function_arg_regno_p): Add support for
ARM_PCS_MS_VARIADIC.
(aarch64_init_builtins): Add support for TARGET_AARCH64_MS_ABI.
(aarch64_ms_variadic_abi_build_builtin_va_list): Setup va_list
for aarch64-w64-mingw32.
(aarch64_build_builtin_va_list): Add support for
TARGET_AARCH64_MS_ABI.
(aarch64_ms_variadic_abi_expand_builtin_va_start): Implement
TARGET_BUILD_BUILTIN_VA_START.
(aarch64_setup_incoming_varargs): Implement
TARGET_SETUP_INCOMING_VARARGS.
(aarch64_mangle_type): Implement TARGET_MANGLE_TYPE.
(aarch64_variadic_abi_strict_argument_naming): Implement
TARGET_STRICT_ARGUMENT_NAMING.
* config/aarch64/aarch64.h
(aarch64_frame): Add new field
unaligned_saved_varargs_size.
(enum arm_pcs): Add new enum option
ARM_PCS_MS_VARIADIC.
* config/aarch64/cygming.h
(SUBTARGET_ATTRIBUTE_TABLE): Add support for ms_abi.
* config/mingw/winnt.cc
(aarch64_handle_ms_abi_attribute): Handle ms_abi attribue.
* config/mingw/winnt.h
(aarch64_handle_ms_abi_attribute): Handle ms_abi attribute.
* config/aarch64/aarch64-abi-ms-protos.h:
(aarch64_arg_partial_bytes): Declare.
(aarch64_ms_variadic_abi_canonical_va_list_type): Declare.
(aarch64_ms_variadic_abi_enum_va_list): Declare.
(aarch64_ms_variadic_abi_fn_abi_va_list): Implement
TARGET_FN_ABI_VA_LIST.
* config/aarch64/aarch64-abi-ms.cc:
(aarch64_arg_partial_bytes): Implement TARGET_ARG_PARTIAL_BYTES.
(aarch64_ms_variadic_abi_canonical_va_list_type): Implement
TARGET_CANONICAL_VA_LIST_TYPE.
(aarch64_ms_variadic_abi_enum_va_list): Implement
TARGET_ENUM_VA_LIST_P.
(aarch64_ms_variadic_abi_fn_abi_va_list): Implement
TARGET_FN_ABI_VA_LIST.
* config/aarch64/t-aarch64-mingw: New Makefile fragment.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/mingw/variadic_hfa.c: New test.
* gcc.target/aarch64/mingw/variadic_hva.c: New test.
* gcc.target/aarch64/mingw/variadic_int.c: New test.
co-authored-by: Radek Barton <[email protected]>
---
gcc/config.gcc | 3 +-
gcc/config/aarch64/aarch64-abi-ms-protos.h | 34 +++
gcc/config/aarch64/aarch64-abi-ms.cc | 106 ++++++++
gcc/config/aarch64/aarch64-builtins.cc | 33 +++
gcc/config/aarch64/aarch64-protos.h | 1 +
gcc/config/aarch64/aarch64.cc | 245 ++++++++++++++++--
gcc/config/aarch64/aarch64.h | 11 +
gcc/config/aarch64/cygming.h | 5 +-
gcc/config/aarch64/t-aarch64-mingw | 25 ++
gcc/config/mingw/winnt.cc | 22 ++
gcc/config/mingw/winnt.h | 1 +
.../gcc.target/aarch64/mingw/variadic_hfa.c | 71 +++++
.../gcc.target/aarch64/mingw/variadic_hva.c | 89 +++++++
.../gcc.target/aarch64/mingw/variadic_int.c | 41 +++
14 files changed, 662 insertions(+), 25 deletions(-)
create mode 100644 gcc/config/aarch64/aarch64-abi-ms-protos.h
create mode 100644 gcc/config/aarch64/aarch64-abi-ms.cc
create mode 100644 gcc/config/aarch64/t-aarch64-mingw
create mode 100644 gcc/testsuite/gcc.target/aarch64/mingw/variadic_hfa.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/mingw/variadic_hva.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/mingw/variadic_int.c
diff --git a/gcc/config.gcc b/gcc/config.gcc
index b46cea869cbd..9d0839768147 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -1293,11 +1293,12 @@ aarch64-*-mingw*)
tm_file="${tm_file} mingw/winnt.h"
tm_file="${tm_file} mingw/winnt-dll.h"
tmake_file="${tmake_file} aarch64/t-aarch64"
+ tmake_file="${tmake_file} aarch64/t-aarch64-mingw"
native_system_header_dir=/mingw/include
target_gtfiles="$target_gtfiles \$(srcdir)/config/mingw/winnt.cc"
target_gtfiles="$target_gtfiles \$(srcdir)/config/mingw/winnt-dll.cc"
extra_options="${extra_options} mingw/cygming.opt mingw/mingw.opt"
- extra_objs="${extra_objs} winnt.o winnt-dll.o"
+ extra_objs="${extra_objs} aarch64-abi-ms.o winnt.o winnt-dll.o"
c_target_objs="${c_target_objs} msformat-c.o"
cxx_target_objs="${cxx_target_objs} msformat-c.o"
d_target_objs="${d_target_objs} winnt-d.o"
diff --git a/gcc/config/aarch64/aarch64-abi-ms-protos.h b/gcc/config/aarch64/aarch64-abi-ms-protos.h
new file mode 100644
index 000000000000..717b60d1a66b
--- /dev/null
+++ b/gcc/config/aarch64/aarch64-abi-ms-protos.h
@@ -0,0 +1,34 @@
+/* Windows specific ABI for AArch64 architecture.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_AARCH64_ABI_MS_PROTOS_H
+#define GCC_AARCH64_ABI_MS_PROTOS_H
+
+extern int aarch64_ms_variadic_abi_enum_va_list (int, const char **,
+ tree *ptree);
+
+extern tree aarch64_ms_variadic_abi_fn_abi_va_list (tree fndecl);
+
+extern tree aarch64_ms_variadic_abi_canonical_va_list_type (tree type);
+
+extern int aarch64_arg_partial_bytes (cumulative_args_t,
+ const function_arg_info &);
+
+#endif
diff --git a/gcc/config/aarch64/aarch64-abi-ms.cc b/gcc/config/aarch64/aarch64-abi-ms.cc
new file mode 100644
index 000000000000..ea0a0e586217
--- /dev/null
+++ b/gcc/config/aarch64/aarch64-abi-ms.cc
@@ -0,0 +1,106 @@
+/* Windows specific ABI for AArch64 architecture.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "backend.h"
+#include "rtl.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "regs.h"
+#include "function-abi.h"
+#include "builtins.h"
+#include "aarch64-abi-ms-protos.h"
+
+/* Iterate through the target-specific builtin types for va_list.
+ IDX denotes the iterator, *PTREE is set to the result type of
+ the va_list builtin, and *PNAME to its internal type.
+ Returns zero if there is no element for this index, otherwise
+ IDX should be increased upon the next call.
+ Note, do not iterate a base builtin's name like __builtin_va_list.
+ Used from c_common_nodes_and_builtins. */
+
+int
+aarch64_ms_variadic_abi_enum_va_list (int idx, const char **pname, tree *ptree)
+{
+ switch (idx)
+ {
+ default:
+ break;
+
+ case 0:
+ *ptree = ms_va_list_type_node;
+ *pname = "__builtin_ms_va_list";
+ return 1;
+ }
+
+ return 0;
+}
+
+/* This function returns the calling abi specific va_list type node.
+ It returns the FNDECL specific va_list type. */
+
+tree
+aarch64_ms_variadic_abi_fn_abi_va_list (tree fndecl)
+{
+ gcc_assert (fndecl != NULL_TREE);
+
+ arm_pcs pcs = (arm_pcs) fndecl_abi (fndecl).id ();
+ if (pcs == ARM_PCS_MS_VARIADIC)
+ return ms_va_list_type_node;
+
+ return std_fn_abi_va_list (fndecl);
+}
+
+/* Returns the canonical va_list type specified by TYPE.
+ If there is no valid TYPE provided, it return NULL_TREE. */
+
+tree
+aarch64_ms_variadic_abi_canonical_va_list_type (tree type)
+{
+ if (lookup_attribute ("ms_abi va_list", TYPE_ATTRIBUTES (type)))
+ return ms_va_list_type_node;
+
+ return NULL_TREE;
+}
+
+/* Implement TARGET_ARG_PARTIAL_BYTES. */
+
+int
+aarch64_arg_partial_bytes (cumulative_args_t pcum_v,
+ const function_arg_info &arg ATTRIBUTE_UNUSED)
+{
+ CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
+
+ if (pcum->pcs_variant != ARM_PCS_MS_VARIADIC)
+ return 0;
+
+ /* Handle the case when argument is split between the last registers and
+ the stack. */
+ if ((pcum->aapcs_reg != NULL_RTX) && (pcum->aapcs_stack_words != 0))
+ return pcum->aapcs_stack_words * UNITS_PER_WORD;
+
+ return 0;
+}
diff --git a/gcc/config/aarch64/aarch64-builtins.cc b/gcc/config/aarch64/aarch64-builtins.cc
index 408099a50e8a..a384ba06c9b6 100644
--- a/gcc/config/aarch64/aarch64-builtins.cc
+++ b/gcc/config/aarch64/aarch64-builtins.cc
@@ -2519,6 +2519,39 @@ aarch64_general_init_builtins (void)
handle_arm_acle_h ();
}
+/* Function to initialize builtin variadic functions for aarch64-w64-mingw32.
+ In this target, variadic functions are handled differently.
+
+ Implements SUBTARGET_INIT_BULITINS. */
+
+void
+aarch64_ms_variadic_abi_init_builtins (void)
+{
+ tree ms_va_ref;
+ tree fnvoid_va_end_ms;
+ tree fnvoid_va_start_ms;
+ tree fnvoid_va_copy_ms;
+ tree fnattr_ms = NULL_TREE;
+
+ fnattr_ms = build_tree_list (get_identifier ("ms_abi"), NULL_TREE);
+ ms_va_ref = build_reference_type (ms_va_list_type_node);
+
+ fnvoid_va_end_ms
+ = build_function_type_list (void_type_node, ms_va_ref, NULL_TREE);
+ fnvoid_va_start_ms
+ = build_varargs_function_type_list (void_type_node, ms_va_ref, NULL_TREE);
+ fnvoid_va_copy_ms
+ = build_function_type_list (void_type_node, ms_va_ref, ms_va_list_type_node,
+ NULL_TREE);
+
+ add_builtin_function ("__builtin_ms_va_start", fnvoid_va_start_ms,
+ BUILT_IN_VA_START, BUILT_IN_NORMAL, NULL, fnattr_ms);
+ add_builtin_function ("__builtin_ms_va_end", fnvoid_va_end_ms,
+ BUILT_IN_VA_END, BUILT_IN_NORMAL, NULL, fnattr_ms);
+ add_builtin_function ("__builtin_ms_va_copy", fnvoid_va_copy_ms,
+ BUILT_IN_VA_COPY, BUILT_IN_NORMAL, NULL, fnattr_ms);
+}
+
/* Implement TARGET_BUILTIN_DECL for the AARCH64_BUILTIN_GENERAL group. */
tree
aarch64_general_builtin_decl (unsigned code, bool)
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index 68f28bdcae89..a11fe149e2f4 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -1140,6 +1140,7 @@ gimple *aarch64_general_gimple_fold_builtin (unsigned int, gcall *,
rtx aarch64_general_expand_builtin (unsigned int, tree, rtx, int);
tree aarch64_general_builtin_decl (unsigned, bool);
tree aarch64_general_builtin_rsqrt (unsigned int);
+void aarch64_ms_variadic_abi_init_builtins (void);
void handle_arm_acle_h (void);
void handle_arm_neon_h (void);
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index a0735504dcca..26689436e073 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -83,6 +83,7 @@
#include "rtlanal.h"
#include "tree-dfa.h"
#include "asan.h"
+#include "aarch64-abi-ms-protos.h"
#include "aarch64-elf-metadata.h"
#include "aarch64-feature-deps.h"
#include "config/arm/aarch-common.h"
@@ -114,6 +115,11 @@
#define HAVE_AS_AEABI_BUILD_ATTRIBUTES 0
#endif
+/* Not on Windows ABI unless explicitly set. */
+#ifndef TARGET_AARCH64_MS_ABI
+#define TARGET_AARCH64_MS_ABI 0
+#endif
+
/* Flags that describe how a function shares certain architectural state
with its callers.
@@ -749,7 +755,8 @@ handle_aarch64_vector_pcs_attribute (tree *node, tree name, tree,
*no_add_attrs = true;
return NULL_TREE;
- /* Rely on the exclusions list for preserve_none. */
+ case ARM_PCS_MS_VARIADIC:
+ /* Rely on the exclusions list for preserve_none. */
case ARM_PCS_PRESERVE_NONE:
case ARM_PCS_TLSDESC:
case ARM_PCS_UNKNOWN:
@@ -2336,11 +2343,29 @@ aarch64_takes_arguments_in_sve_regs_p (const_tree fntype)
return false;
}
+/* Return the descriptor of the Windows Arm64 variadic function call ABI. */
+
+static const predefined_function_abi &
+aarch64_ms_variadic_abi (void)
+{
+ predefined_function_abi &ms_variadic_abi = function_abis[ARM_PCS_MS_VARIADIC];
+ if (!ms_variadic_abi.initialized_p ())
+ {
+ HARD_REG_SET full_reg_clobbers
+ = default_function_abi.full_reg_clobbers ();
+ ms_variadic_abi.initialize (ARM_PCS_MS_VARIADIC, full_reg_clobbers);
+ }
+ return ms_variadic_abi;
+}
+
/* Implement TARGET_FNTYPE_ABI. */
static const predefined_function_abi &
aarch64_fntype_abi (const_tree fntype)
{
+ if (TARGET_AARCH64_MS_ABI && stdarg_p (fntype))
+ return aarch64_ms_variadic_abi ();
+
if (lookup_attribute ("aarch64_vector_pcs", TYPE_ATTRIBUTES (fntype)))
return aarch64_simd_abi ();
@@ -2554,14 +2579,18 @@ aarch64_reg_save_mode (unsigned int regno)
if (FP_REGNUM_P (regno))
switch (crtl->abi->id ())
{
+ case ARM_PCS_AAPCS64:
+ /* Only the low 64 bits are saved by the base PCS. */
case ARM_PCS_PRESERVE_NONE:
/* In preserve_none all fpr registers are caller saved, so the choice
here should not matter. Nevertheless, fall back to the base AAPCS
for consistency. */
- case ARM_PCS_AAPCS64:
- /* Only the low 64 bits are saved by the base PCS. */
return DFmode;
+ case ARM_PCS_MS_VARIADIC:
+ /* Windows only uses GP registers for variadic arguments. */
+ return DImode;
+
case ARM_PCS_SIMD:
/* The vector PCS saves the low 128 bits (which is the full
register on non-SVE targets). */
@@ -7342,6 +7371,7 @@ num_pcs_arg_regs (enum arm_pcs pcs)
case ARM_PCS_PRESERVE_NONE:
return NUM_PRESERVE_NONE_ARG_REGS;
case ARM_PCS_AAPCS64:
+ case ARM_PCS_MS_VARIADIC:
case ARM_PCS_SIMD:
case ARM_PCS_SVE:
case ARM_PCS_TLSDESC:
@@ -7366,6 +7396,7 @@ get_pcs_arg_reg (enum arm_pcs pcs, int num)
case ARM_PCS_PRESERVE_NONE:
return ARM_PCS_PRESERVE_NONE_REGISTERS[num];
case ARM_PCS_AAPCS64:
+ case ARM_PCS_MS_VARIADIC:
case ARM_PCS_SIMD:
case ARM_PCS_SVE:
case ARM_PCS_TLSDESC:
@@ -7375,6 +7406,77 @@ get_pcs_arg_reg (enum arm_pcs pcs, int num)
gcc_unreachable ();
}
+static int
+aarch64_arg_size (const function_arg_info &arg)
+{
+ HOST_WIDE_INT size;
+
+ /* Size in bytes, rounded to the nearest multiple of 8 bytes. */
+ if (arg.type)
+ size = int_size_in_bytes (arg.type);
+ else
+ /* No frontends can create types with variable-sized modes, so we
+ shouldn't be asked to pass or return them. */
+ size = GET_MODE_SIZE (arg.mode).to_constant ();
+
+ return ROUND_UP (size, UNITS_PER_WORD);
+}
+
+/* The Windows Arm64 variadic function call ABI uses only C.12-C15 rules.
+ See: https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions#addendum-variadic-functions. */
+
+static void
+aarch64_ms_variadic_abi_layout_arg (cumulative_args_t pcum_v,
+ const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
+ auto mode = arg.mode;
+ auto ncrn = pcum->aapcs_ncrn;
+ HOST_WIDE_INT size = aarch64_arg_size (arg);
+ auto nregs = size / UNITS_PER_WORD;
+
+ if (ncrn < NUM_ARG_REGS)
+ {
+ /* The argument bytes are copied to the core registers. */
+ if (nregs == 1 || GET_MODE_CLASS (mode) == MODE_INT)
+ pcum->aapcs_reg = gen_rtx_REG (mode, R0_REGNUM + ncrn);
+ else
+ {
+ /* Handle the case when argument is split
+ between the last registers and the
+ stack. */
+ if (ncrn + nregs > NUM_ARG_REGS)
+ {
+ pcum->aapcs_stack_words = ncrn + nregs - NUM_ARG_REGS;
+ nregs -= pcum->aapcs_stack_words;
+ }
+
+ /* Generate load arg to registers instructions. */
+ rtx par = gen_rtx_PARALLEL (mode, rtvec_alloc (nregs));
+ for (auto i = 0; i < nregs; i++)
+ {
+ rtx tmp = gen_rtx_REG (word_mode, R0_REGNUM + ncrn + i);
+ tmp = gen_rtx_EXPR_LIST (VOIDmode, tmp,
+ GEN_INT (i * UNITS_PER_WORD));
+ XVECEXP (par, 0, i) = tmp;
+ }
+ pcum->aapcs_reg = par;
+ }
+
+ pcum->aapcs_nextncrn = ncrn + nregs;
+ }
+ else
+ {
+ /* The remaining arguments are passed on stack; record the needed
+ number of words for this argument and align the total size if
+ necessary. */
+ pcum->aapcs_nextncrn = NUM_ARG_REGS;
+ pcum->aapcs_stack_words = nregs;
+ }
+
+ pcum->aapcs_arg_processed = true;
+}
+
/* Layout a function argument according to the AAPCS64 rules. The rule
numbers refer to the rule numbers in the AAPCS64. ORIG_MODE is the
mode that was originally given to us by the target hook, whereas the
@@ -7398,6 +7500,12 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
if (pcum->aapcs_arg_processed)
return;
+ if (pcum->pcs_variant == ARM_PCS_MS_VARIADIC)
+ {
+ aarch64_ms_variadic_abi_layout_arg (pcum_v, arg);
+ return;
+ }
+
bool warn_pcs_change
= (warn_psabi
&& !pcum->silent_p
@@ -7515,15 +7623,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
&& (aarch64_some_values_include_pst_objects_p (type)
|| (vec_flags & VEC_PARTIAL)));
- /* Size in bytes, rounded to the nearest multiple of 8 bytes. */
- if (type)
- size = int_size_in_bytes (type);
- else
- /* No frontends can create types with variable-sized modes, so we
- shouldn't be asked to pass or return them. */
- size = GET_MODE_SIZE (mode).to_constant ();
- size = ROUND_UP (size, UNITS_PER_WORD);
-
+ size = aarch64_arg_size (arg);
allocate_ncrn = (type) ? !(FLOAT_TYPE_P (type)) : !FLOAT_MODE_P (mode);
allocate_nvrn = aarch64_vfp_is_call_candidate (pcum_v,
mode,
@@ -7765,10 +7865,10 @@ aarch64_function_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
gcc_assert (pcum->pcs_variant == ARM_PCS_AAPCS64
+ || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE
+ || pcum->pcs_variant == ARM_PCS_MS_VARIADIC
|| pcum->pcs_variant == ARM_PCS_SIMD
- || pcum->pcs_variant == ARM_PCS_SVE
- || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE);
-
+ || pcum->pcs_variant == ARM_PCS_SVE);
if (arg.end_marker_p ())
{
rtx abi_cookie = aarch64_gen_callee_cookie (pcum->isa_mode,
@@ -7860,7 +7960,8 @@ aarch64_function_arg_advance (cumulative_args_t pcum_v,
if (pcum->pcs_variant == ARM_PCS_AAPCS64
|| pcum->pcs_variant == ARM_PCS_SIMD
|| pcum->pcs_variant == ARM_PCS_SVE
- || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE)
+ || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE
+ || pcum->pcs_variant == ARM_PCS_MS_VARIADIC)
{
aarch64_layout_arg (pcum_v, arg);
if (pcum->aapcs_reg && aarch64_call_switches_pstate_sm (pcum->isa_mode))
@@ -7899,6 +8000,7 @@ aarch64_function_arg_regno_p (unsigned regno)
switch (pcs)
{
case ARM_PCS_AAPCS64:
+ case ARM_PCS_MS_VARIADIC:
case ARM_PCS_SIMD:
case ARM_PCS_SVE:
case ARM_PCS_TLSDESC:
@@ -16425,6 +16527,13 @@ aarch64_init_builtins ()
{
aarch64_general_init_builtins ();
aarch64_sve::init_builtins ();
+ if (TARGET_AARCH64_MS_ABI)
+ {
+ do
+ {
+ aarch64_ms_variadic_abi_init_builtins ();
+ } while (0);
+ }
#ifdef SUBTARGET_INIT_BUILTINS
SUBTARGET_INIT_BUILTINS;
#endif
@@ -21798,6 +21907,24 @@ aarch64_load_tp (rtx target)
return target;
}
+/* Windows Arm64 variadic function call ABI specific va_list type node. */
+tree ms_va_list_type_node = NULL_TREE;
+
+/* Setup the builtin va_list data type and for 64-bit the additional
+ calling convention specific va_list data types. */
+
+static tree
+aarch64_ms_variadic_abi_build_builtin_va_list (void)
+{
+ /* For MS_ABI we use plain pointer to argument area. */
+ tree char_ptr_type = build_pointer_type (char_type_node);
+ tree attr = tree_cons (get_identifier ("ms_abi va_list"), NULL_TREE,
+ TYPE_ATTRIBUTES (char_ptr_type));
+ ms_va_list_type_node = build_type_attribute_variant (char_ptr_type, attr);
+
+ return ms_va_list_type_node;
+}
+
/* On AAPCS systems, this is the "struct __va_list". */
static GTY(()) tree va_list_type;
@@ -21813,11 +21940,17 @@ static GTY(()) tree va_list_type;
void *__vr_top;
int __gr_offs;
int __vr_offs;
- }; */
+ };
+
+ Windows ABI is handled using
+ aarch64_ms_variadic_abi_build_builtin_va_list (void). */
static tree
aarch64_build_builtin_va_list (void)
{
+ if (TARGET_AARCH64_MS_ABI)
+ return aarch64_ms_variadic_abi_build_builtin_va_list ();
+
tree va_list_name;
tree f_stack, f_grtop, f_vrtop, f_groff, f_vroff;
@@ -21881,10 +22014,29 @@ aarch64_build_builtin_va_list (void)
return va_list_type;
}
+static void
+aarch64_ms_variadic_abi_expand_builtin_va_start (tree valist, rtx nextarg)
+{
+ rtx va_r = expand_expr (valist, NULL_RTX, VOIDmode, EXPAND_WRITE);
+
+ /* ??? Should we initialize and use cfun->va_list_gpr_size instead of
+ * defining single purpose
+ * cfun->machine->frame.unaligned_saved_varargs_size field.
+ * Currently, the cfun->va_list_gpr_size contains only value 255. */
+ int offset = cfun->machine->frame.unaligned_saved_varargs_size;
+ nextarg = plus_constant (GET_MODE (nextarg), nextarg, -offset);
+
+ convert_move (va_r, nextarg, 0);
+}
+
/* Implement TARGET_EXPAND_BUILTIN_VA_START. */
+
static void
-aarch64_expand_builtin_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
+aarch64_expand_builtin_va_start (tree valist, rtx nextarg)
{
+ if (TARGET_AARCH64_MS_ABI)
+ return aarch64_ms_variadic_abi_expand_builtin_va_start (valist, nextarg);
+
const CUMULATIVE_ARGS *cum;
tree f_stack, f_grtop, f_vrtop, f_groff, f_vroff;
tree stack, grtop, vrtop, groff, vroff;
@@ -21967,6 +22119,7 @@ aarch64_expand_builtin_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
/* Implement TARGET_GIMPLIFY_VA_ARG_EXPR. */
+#if TARGET_AARCH64_MS_ABI == 0
static tree
aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
gimple_seq *post_p ATTRIBUTE_UNUSED)
@@ -22259,6 +22412,7 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
return addr;
}
+#endif
/* Implement TARGET_SETUP_INCOMING_VARARGS. */
@@ -22289,7 +22443,8 @@ aarch64_setup_incoming_varargs (cumulative_args_t cum_v,
vr_saved = MIN (NUM_FP_ARG_REGS - local_cum.aapcs_nvrn,
cfun->va_list_fpr_size / UNITS_PER_VREG);
- if (!TARGET_FLOAT)
+ /* Windows variadic function calls ABI never uses vector registers. */
+ if (TARGET_AARCH64_MS_ABI || !TARGET_FLOAT)
{
gcc_assert (local_cum.aapcs_nvrn == 0);
vr_saved = 0;
@@ -22354,8 +22509,9 @@ aarch64_setup_incoming_varargs (cumulative_args_t cum_v,
/* We don't save the size into *PRETEND_SIZE because we want to avoid
any complication of having crtl->args.pretend_args_size changed. */
+ cfun->machine->frame.unaligned_saved_varargs_size = gr_saved * UNITS_PER_WORD;
cfun->machine->frame.saved_varargs_size
- = (ROUND_UP (gr_saved * UNITS_PER_WORD,
+ = (ROUND_UP (cfun->machine->frame.unaligned_saved_varargs_size,
STACK_BOUNDARY / BITS_PER_UNIT)
+ vr_saved * UNITS_PER_VREG);
}
@@ -23166,8 +23322,11 @@ static const char *
aarch64_mangle_type (const_tree type)
{
/* The AArch64 ABI documents say that "__va_list" has to be
- mangled as if it is in the "std" namespace. */
- if (lang_hooks.types_compatible_p (CONST_CAST_TREE (type), va_list_type))
+ mangled as if it is in the "std" namespace.
+ The Windows Arm64 ABI uses just an address of the first variadic
+ argument. */
+ if (!TARGET_AARCH64_MS_ABI
+ && lang_hooks.types_compatible_p (CONST_CAST_TREE (type), va_list_type))
return "St9__va_list";
/* Half-precision floating point types. */
@@ -25815,6 +25974,28 @@ aarch64_post_cfi_startproc (FILE *f, tree ignored ATTRIBUTE_UNUSED)
asm_fprintf (f, "\t.cfi_b_key_frame\n");
}
+/* Implement TARGET_STRICT_ARGUMENT_NAMING.
+
+ Return true if the location where a function argument is passed
+ depends on whether or not it is a named argument.
+
+ For Windows ABI of variadic function calls, treat the named arguments as
+ unnamed as they are handled the same way as variadic arguments. */
+
+static bool
+aarch64_variadic_abi_strict_argument_naming (cumulative_args_t pcum_v)
+{
+ if (!TARGET_AARCH64_MS_ABI)
+ return hook_bool_CUMULATIVE_ARGS_true (pcum_v);
+
+ CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
+
+ if (pcum->pcs_variant == ARM_PCS_MS_VARIADIC)
+ return false;
+
+ return hook_bool_CUMULATIVE_ARGS_true (pcum_v);
+}
+
/* Implements TARGET_ASM_FILE_START. Output the assembly header. */
static void
@@ -32563,6 +32744,21 @@ aarch64_run_selftests (void)
#undef TARGET_EXPAND_BUILTIN_VA_START
#define TARGET_EXPAND_BUILTIN_VA_START aarch64_expand_builtin_va_start
+#if TARGET_AARCH64_MS_ABI == 1
+#undef TARGET_ENUM_VA_LIST_P
+#define TARGET_ENUM_VA_LIST_P aarch64_ms_variadic_abi_enum_va_list
+
+#undef TARGET_FN_ABI_VA_LIST
+#define TARGET_FN_ABI_VA_LIST aarch64_ms_variadic_abi_fn_abi_va_list
+
+#undef TARGET_CANONICAL_VA_LIST_TYPE
+#define TARGET_CANONICAL_VA_LIST_TYPE \
+ aarch64_ms_variadic_abi_canonical_va_list_type
+
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES aarch64_arg_partial_bytes
+#endif
+
#undef TARGET_FOLD_BUILTIN
#define TARGET_FOLD_BUILTIN aarch64_fold_builtin
@@ -32601,8 +32797,10 @@ aarch64_run_selftests (void)
#undef TARGET_GIMPLE_FOLD_BUILTIN
#define TARGET_GIMPLE_FOLD_BUILTIN aarch64_gimple_fold_builtin
+#if TARGET_AARCH64_MS_ABI == 0
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR aarch64_gimplify_va_arg_expr
+#endif
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS aarch64_init_builtins
@@ -33024,7 +33222,8 @@ aarch64_libgcc_floating_mode_supported_p
#define TARGET_ASM_POST_CFI_STARTPROC aarch64_post_cfi_startproc
#undef TARGET_STRICT_ARGUMENT_NAMING
-#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
+#define TARGET_STRICT_ARGUMENT_NAMING \
+ aarch64_variadic_abi_strict_argument_naming
#undef TARGET_MODE_EMIT
#define TARGET_MODE_EMIT aarch64_mode_emit
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index cb220c82c4eb..5a1d5a94670f 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -1017,6 +1017,9 @@ struct GTY (()) aarch64_frame
STACK_BOUNDARY. */
HOST_WIDE_INT saved_varargs_size;
+ /* The same as above except it is the original unaligned stack size. */
+ HOST_WIDE_INT unaligned_saved_varargs_size;
+
/* The number of bytes between the bottom of the static frame (the bottom
of the outgoing arguments) and the bottom of the register save area.
This value is always a multiple of STACK_BOUNDARY. */
@@ -1182,6 +1185,11 @@ enum arm_pcs
ARM_PCS_TLSDESC, /* For targets of tlsdesc calls. */
ARM_PCS_PRESERVE_NONE, /* PCS variant with no call-preserved
registers except X29. */
+ ARM_PCS_MS_VARIADIC, /* PCS variant with no call-preserved
+ differently.
+ All composites are treated alike.
+ SIMD and floating-point registers
+ aren't used. */
ARM_PCS_UNKNOWN
};
@@ -1565,6 +1573,9 @@ extern GTY(()) tree aarch64_fp16_ptr_type_node;
bfloat16_type_node. Defined in aarch64-builtins.cc. */
extern GTY(()) tree aarch64_bf16_ptr_type_node;
+/* Windows Arm64 variadic function call ABI specific va_list type node. */
+extern GTY(()) tree ms_va_list_type_node;
+
/* The generic unwind code in libgcc does not initialize the frame pointer.
So in order to unwind a function using a frame pointer, the very first
function that is unwound must save the frame pointer. That way the frame
diff --git a/gcc/config/aarch64/cygming.h b/gcc/config/aarch64/cygming.h
index 7e2203c3e927..1c7f8f58e645 100644
--- a/gcc/config/aarch64/cygming.h
+++ b/gcc/config/aarch64/cygming.h
@@ -205,7 +205,10 @@ still needed for compilation. */
#define SUBTARGET_ATTRIBUTE_TABLE \
{ "selectany", 0, 0, true, false, false, false, \
- mingw_handle_selectany_attribute, NULL }
+ mingw_handle_selectany_attribute, NULL }, \
+ { "ms_abi", 0, 0, false, true, true, true, \
+ aarch64_handle_ms_abi_attribute, NULL }, \
+ { "ms_abi va_list", 0, 0, false, false, false, false, NULL, NULL }
#undef SUB_TARGET_RECORD_STUB
#define SUB_TARGET_RECORD_STUB(NAME, DECL) mingw_pe_record_stub((NAME), \
diff --git a/gcc/config/aarch64/t-aarch64-mingw b/gcc/config/aarch64/t-aarch64-mingw
new file mode 100644
index 000000000000..fea7ae583fb8
--- /dev/null
+++ b/gcc/config/aarch64/t-aarch64-mingw
@@ -0,0 +1,25 @@
+# Windows specific ABI for AArch64 architecture.
+# Copyright (C) 2025 Free Software Foundation, Inc.
+# Contributed by ARM Ltd.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+aarch64-abi-ms.o: \
+ $(srcdir)/config/aarch64/aarch64-abi-ms.cc \
+ $(TREE_H)
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+ $(srcdir)/config/aarch64/aarch64-abi-ms.cc
diff --git a/gcc/config/mingw/winnt.cc b/gcc/config/mingw/winnt.cc
index f22496615eda..b51fd8e9cc6a 100644
--- a/gcc/config/mingw/winnt.cc
+++ b/gcc/config/mingw/winnt.cc
@@ -339,6 +339,28 @@ mingw_pe_encode_section_info (tree decl, rtx rtl, int first)
SYMBOL_REF_FLAGS (symbol) = flags;
}
+/* Handle a "ms_abi" attribute; arguments as in struct
+ attribute_spec.handler. */
+
+tree
+aarch64_handle_ms_abi_attribute (tree *node, tree name, tree, int,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_TYPE
+ && TREE_CODE (*node) != METHOD_TYPE
+ && TREE_CODE (*node) != FIELD_DECL
+ && TREE_CODE (*node) != TYPE_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+
+ return NULL_TREE;
+ }
+
+ return NULL_TREE;
+}
+
bool
i386_pe_binds_local_p (const_tree exp)
diff --git a/gcc/config/mingw/winnt.h b/gcc/config/mingw/winnt.h
index 23f4dc94ec5f..ccb5e58a88b6 100644
--- a/gcc/config/mingw/winnt.h
+++ b/gcc/config/mingw/winnt.h
@@ -20,6 +20,7 @@ http://www.gnu.org/licenses/. */
#ifndef USED_FOR_TARGET
+extern tree aarch64_handle_ms_abi_attribute (tree *, tree, tree, int, bool *);
extern tree mingw_handle_selectany_attribute (tree *, tree, tree, int, bool *);
extern void mingw_pe_asm_named_section (const char *, unsigned int, tree);
diff --git a/gcc/testsuite/gcc.target/aarch64/mingw/variadic_hfa.c b/gcc/testsuite/gcc.target/aarch64/mingw/variadic_hfa.c
new file mode 100644
index 000000000000..5b3e3ae3fde4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mingw/variadic_hfa.c
@@ -0,0 +1,71 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c99" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <stdarg.h>
+
+typedef struct {
+ double x;
+ double y;
+} point2d;
+
+point2d accumulate(int count, ...) {
+ int i;
+ va_list ap;
+ va_start(ap, count);
+
+ point2d acc = {0.0, 0.0};
+ for (i = 0; i < count; ++i) {
+ point2d v = va_arg(ap, point2d);
+ acc.x += v.x;
+ acc.y += v.y;
+ }
+
+ va_end(ap);
+ return acc;
+}
+
+/**
+ * For aarch64-w64-mingw32 target, the Homogeneous Floating-point Aggregate
+ * (HFA) types are not treated specially.
+ *
+ * This is in contrast to to aarch64-linux-gnu target where double float args
+ * would be loaded into 64 bit D registers.
+ */
+
+/*
+** main:
+** ...
+** fmov d\d+, 2.0e\+0
+** str d\d+, \[sp, \d+\]
+** fmov d\d+, 1.0e\+0
+** str d\d+, \[sp, \d+\]
+** fmov d\d+, 4.0e\+0
+** str d\d+, \[sp, \d+\]
+** fmov d\d+, 3.0e\+0
+** str d\d+, \[sp, \d+\]
+** fmov d\d+, 6.0e\+0
+** str d\d+, \[sp, \d+\]
+** fmov d\d+, 5.0e\+0
+** str d\d+, \[sp, \d+\]
+** fmov d\d+, 8.0e\+0
+** str d\d+, \[sp, \d+\]
+** fmov d\d+, 7.0e\+0
+** str d\d+, \[sp, \d+\]
+** ldp x\d+, x\d+, \[sp, \d+\]
+** ldp x\d+, x\d+, \[sp, \d+\]
+** ldp x\d+, x\d+, \[sp, \d+\]
+** ldp x\d+, x\d+, \[sp, \d+\]
+** ...
+*/
+int main()
+{
+ point2d p1 = {2.0, 1.0};
+ point2d p2 = {4.0, 3.0};
+ point2d p3 = {6.0, 5.0};
+ point2d p4 = {8.0, 7.0};
+
+ accumulate (4, p1, p2, p3, p4);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/mingw/variadic_hva.c b/gcc/testsuite/gcc.target/aarch64/mingw/variadic_hva.c
new file mode 100644
index 000000000000..7c690d384721
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mingw/variadic_hva.c
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c99" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_neon.h>
+#include <stdarg.h>
+
+typedef struct {
+ float32x4_t a;
+ float32x4_t b;
+ float32x4_t c;
+ float32x4_t d;
+} mat4x4;
+
+mat4x4 accumulate(int count, ...) {
+ va_list va;
+ va_start(va, count);
+
+ mat4x4 result = {
+ vdupq_n_f32(0.0f),
+ vdupq_n_f32(0.0f),
+ vdupq_n_f32(0.0f),
+ vdupq_n_f32(0.0f)
+ };
+
+ for (int i = 0; i < count; ++i) {
+ mat4x4 v = va_arg(va, mat4x4);
+ result.a = vaddq_f32(result.a, v.a);
+ result.b = vaddq_f32(result.b, v.b);
+ result.c = vaddq_f32(result.c, v.c);
+ result.d = vaddq_f32(result.d, v.d);
+ }
+
+ va_end(va);
+ return result;
+}
+
+
+/**
+ * For aarch64-w64-mingw32 target, the Homogeneous Vector Aggregate (HVA) types
+ * are not treated specially.
+ *
+ * This is in contrast to to aarch64-linux-gnu target where float32x4n args
+ * would be loaded into 128 bit Q registers.
+ */
+
+
+/*
+** main:
+** ...
+** ldr q\d+, \[x\d+, #:lo\d+:\.LC\d+\]
+** str q\d+, \[sp, \d+\]
+** ...
+** ldr q\d+, \[x\d+, #:lo\d+:\.LC\d+\]
+** str q\d+, \[sp, \d+\]
+** ...
+** ldr q\d+, \[x\d+, #:lo\d+:\.LC\d+\]
+** str q\d+, \[sp, \d+\]
+** ...
+** ldr q\d+, \[x\d+, #:lo\d+:\.LC\d+\]
+** str q\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+** ldr x\d+, \[sp, \d+\]
+** ...
+*/
+int main()
+{
+ float32x4_t x = {1.0, 2.0, 3.0, 4.0};
+ float32x4_t y = {2.0, 3.0, 4.0, 5.0};
+ float32x4_t z = {3.0, 4.0, 5.0, 6.0};
+ float32x4_t w = {4.0, 5.0, 6.0, 7.0};
+
+ accumulate (4, x, y, z, w);
+ return 0;
+}
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/aarch64/mingw/variadic_int.c b/gcc/testsuite/gcc.target/aarch64/mingw/variadic_int.c
new file mode 100644
index 000000000000..bb4e2f12e7ed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mingw/variadic_int.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c99" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <stdarg.h>
+
+/*
+** sum:
+** ...
+** str w0, \[sp, \d+\]
+** str x1, \[sp, \d+\]
+** str x2, \[sp, \d+\]
+** str x3, \[sp, \d+\]
+** str x4, \[sp, \d+\]
+** str x5, \[sp, \d+\]
+** str x6, \[sp, \d+\]
+** str x7, \[sp, \d+\]
+** add x0, sp, \d+
+** sub x0, x0, #\d+
+** str x0, \[sp, \d+\]
+** str wzr, \[sp, \d+\]
+** str wzr, \[sp, \d+\]
+** ...
+*/
+int sum(int count, ...) {
+ va_list args;
+
+ va_start(args, count);
+
+ int total = 0;
+ for (int i = 0; i < count; i++)
+ {
+ total += va_arg(args, int);
+ }
+
+ va_end(args);
+
+ return total;
+}
+
+/* { dg-final { scan-assembler-not "str\tq\[0-9\]+, \[sp, \[0-9\]+\]*" } } */
\ No newline at end of file