From: Saurabh Jha Date: Thu, 20 Nov 2025 15:14:24 +0000 (+0000) Subject: aarch64: mingw: Implement support for variadic ABI X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c40c67095a97f0335b4dbe100ed788ede1d79e3d;p=thirdparty%2Fgcc.git aarch64: mingw: Implement support for variadic ABI 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 --- diff --git a/gcc/config.gcc b/gcc/config.gcc index e17354b3e65..fb465dac147 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 00000000000..717b60d1a66 --- /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 + . */ + +#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 00000000000..ea0a0e58621 --- /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 + . */ + +#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 408099a50e8..a384ba06c9b 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 11c2938558d..da1d734a689 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -1139,6 +1139,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 f16c91cd44b..db914417c1a 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" @@ -116,6 +117,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. @@ -751,7 +757,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: @@ -2338,11 +2345,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 (); @@ -2556,14 +2581,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). */ @@ -7344,6 +7373,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: @@ -7368,6 +7398,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: @@ -7377,6 +7408,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 @@ -7400,6 +7502,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 @@ -7517,15 +7625,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, @@ -7767,10 +7867,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, @@ -7862,7 +7962,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)) @@ -7901,6 +8002,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: @@ -16427,6 +16529,10 @@ aarch64_init_builtins () { aarch64_general_init_builtins (); aarch64_sve::init_builtins (); + if (TARGET_AARCH64_MS_ABI) + { + aarch64_ms_variadic_abi_init_builtins (); + } #ifdef SUBTARGET_INIT_BUILTINS SUBTARGET_INIT_BUILTINS; #endif @@ -21818,6 +21924,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; @@ -21833,11 +21957,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; @@ -21901,10 +22031,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; @@ -21987,6 +22136,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) @@ -22279,6 +22429,7 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, return addr; } +#endif /* Implement TARGET_SETUP_INCOMING_VARARGS. */ @@ -22309,7 +22460,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; @@ -22374,8 +22526,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); } @@ -23186,8 +23339,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. */ @@ -25835,6 +25991,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 @@ -32586,6 +32764,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 @@ -32624,8 +32817,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 @@ -33047,7 +33242,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 cb220c82c4e..5a1d5a94670 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 7e2203c3e92..1c7f8f58e64 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 00000000000..fea7ae583fb --- /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 +# . + +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 f22496615ed..b51fd8e9cc6 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 23f4dc94ec5..ccb5e58a88b 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 00000000000..5b3e3ae3fde --- /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 + +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 00000000000..7c690d38472 --- /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 +#include + +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 00000000000..bb4e2f12e7e --- /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 + +/* +** 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