On Thu, Nov 6, 2025 at 7:07 AM Alfie Richards <[email protected]> wrote:
>
> Hi All,
>
> Updated this patch to use r15 for argument passing (except on windows targets)
> which I missed.
>
> Updated the wording of the documentation and a comment slightly as it had 
> caused
> some confusion.
>
> Reg tested on aarch64-linux-gnu.
>
> Okay for master?

There seems to be a big (10%) compile time increase in compiling
trampv3 at `-O0 -g` due to this patch:
https://lnt.opensuse.org/db_default/v4/CPP/graph?plot.0=358.576.8

pr12850.cpp also increase ~10%
pr36439.cpp increase is slightly less at ~4%
pr12392.cpp increase is even less at 2%.

At -O2, the compile time seems the same for these.
I have not looked into why ow how the increase happened. Just reporting it.
Though since it happens at -O0, this makes me think there is a lot of
extra stuff happening either for function calls or for each function
that is going to be emitted.

Thanks,
Andrew

>
> Alfie
>
> -- >8 --
>
> When applied to a function preserve_none changes the procedure call standard
> such that all registers except stack pointer, frame register, and link 
> register
> are caller saved. Additionally, it changes the argument passing registers.
>
>         PR target/118328
>
> gcc/ChangeLog:
>
>         * config/aarch64/aarch64.cc (handle_aarch64_vector_pcs_attribute):
>         Add handling for ARM_PCS_PRESERVE_NONE.
>         (aarch64_pcs_exclusions): New definition.
>         (aarch64_gnu_attributes): Add entry for preserve_none and add
>         aarch64_pcs_exclusions to aarch64_vector_pcs entry.
>         (aarch64_preserve_none_abi): New function.
>         (aarch64_fntype_abi): Add handling for preserve_none.
>         (aarch64_reg_save_mode): Add handling for ARM_PCS_PRESERVE_NONE.
>         (aarch64_hard_regno_call_part_clobbered): Add handling for
>         ARM_PCS_PRESERVE_NONE.
>         (num_pcs_arg_regs): New helper function.
>         (get_pcs_arg_reg): New helper function.
>         (aarch64_function_ok_for_sibcall): Add handling for 
> ARM_PCS_PRESERVE_NONE.
>         (aarch64_layout_arg): Add preserve_none argument lauout..
>         (function_arg_preserve_none_regno_p): New helper function.
>         (aarch64_function_arg): Update to handle preserve_none.
>         (function_arg_preserve_none_regno_p): Update logic for preserve_none.
>         (aarch64_expand_builtin_va_start): Add preserve_none layout.
>         (aarch64_setup_incoming_varargs): Add preserve_none layout.
>         (aarch64_is_variant_pcs): Update for case of ARM_PCS_PRESERVE_NONE.
>         (aarch64_comp_type_attributes): Add preserve_none.
>         * config/aarch64/aarch64.h (NUM_PRESERVE_NONE_ARG_REGS): New macro.
>         (PRESERVE_NONE_REGISTERS): New macro.
>         (enum arm_pcs): Add ARM_PCS_PRESERVE_NONE.
>         * doc/extend.texi (preserve_none): Add docs for new attribute.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/aarch64/preserve_none_1.c: New test.
>         * gcc.target/aarch64/preserve_none_mingw_1.c: New test.
>         * gcc.target/aarch64/preserve_none_2.c: New test.
>         * gcc.target/aarch64/preserve_none_3.c: New test.
>         * gcc.target/aarch64/preserve_none_4.c: New test.
>         * gcc.target/aarch64/preserve_none_5.c: New test.
>         * gcc.target/aarch64/preserve_none_6.c: New test.
> ---
>  gcc/config/aarch64/aarch64.cc                 | 179 ++++++++++++++++--
>  gcc/config/aarch64/aarch64.h                  |  27 +++
>  gcc/doc/extend.texi                           |  21 ++
>  .../gcc.target/aarch64/preserve_none_1.c      | 143 ++++++++++++++
>  .../gcc.target/aarch64/preserve_none_2.c      |  49 +++++
>  .../gcc.target/aarch64/preserve_none_3.c      | 114 +++++++++++
>  .../gcc.target/aarch64/preserve_none_4.c      |  99 ++++++++++
>  .../gcc.target/aarch64/preserve_none_5.c      |  47 +++++
>  .../gcc.target/aarch64/preserve_none_6.c      |  76 ++++++++
>  .../aarch64/preserve_none_mingw_1.c           |  93 +++++++++
>  10 files changed, 828 insertions(+), 20 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_1.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_2.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_3.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_4.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_5.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_6.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_mingw_1.c
>
> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> index b86064148fe..e3776fbb543 100644
> --- a/gcc/config/aarch64/aarch64.cc
> +++ b/gcc/config/aarch64/aarch64.cc
> @@ -749,6 +749,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_PRESERVE_NONE:
>      case ARM_PCS_TLSDESC:
>      case ARM_PCS_UNKNOWN:
>        break;
> @@ -851,6 +853,16 @@ handle_arm_shared (tree *node, tree name, tree args,
>    return NULL_TREE;
>  }
>
> +/* Mutually-exclusive function type attributes for various PCS variants.  */
> +static const struct attribute_spec::exclusions aarch64_pcs_exclusions[] =
> +{
> +  /* Attribute name     exclusion applies to:
> +                       function, type, variable */
> +  { "aarch64_vector_pcs", false, true, false },
> +  { "preserve_none", false, true, false },
> +  { NULL, false, false, false }
> +};
> +
>  /* Mutually-exclusive function type attributes for controlling PSTATE.SM.  */
>  static const struct attribute_spec::exclusions attr_streaming_exclusions[] =
>  {
> @@ -867,7 +879,10 @@ static const attribute_spec aarch64_gnu_attributes[] =
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "aarch64_vector_pcs", 0, 0, false, true,  true,  true,
> -                         handle_aarch64_vector_pcs_attribute, NULL },
> +                         handle_aarch64_vector_pcs_attribute,
> +                         aarch64_pcs_exclusions },
> +  { "preserve_none",      0, 0, false, true,  true,  true,  NULL,
> +                         aarch64_pcs_exclusions },
>    { "indirect_return",    0, 0, false, true, true, true, NULL, NULL },
>    { "arm_sve_vector_bits", 1, 1, false, true,  false, true,
>                           aarch64_sve::handle_arm_sve_vector_bits_attribute,
> @@ -1317,6 +1332,23 @@ aarch64_sve_abi (void)
>    return sve_abi;
>  }
>
> +/* Return the descriptor of the preserve_none PCS.  */
> +
> +static const predefined_function_abi &
> +aarch64_preserve_none_abi (void)
> +{
> +  auto &preserve_none_abi = function_abis[ARM_PCS_PRESERVE_NONE];
> +  if (!preserve_none_abi.initialized_p ())
> +    {
> +      HARD_REG_SET preserved_regs = {};
> +      if (!CALL_USED_X18)
> +       SET_HARD_REG_BIT (preserved_regs, R18_REGNUM);
> +      auto full_reg_clobbers = reg_class_contents[ALL_REGS] & 
> ~preserved_regs;
> +      preserve_none_abi.initialize (ARM_PCS_PRESERVE_NONE, 
> full_reg_clobbers);
> +    }
> +  return preserve_none_abi;
> +}
> +
>  /* If X is an UNSPEC_SALT_ADDR expression, return the address that it
>     wraps, otherwise return X itself.  */
>
> @@ -2312,6 +2344,9 @@ aarch64_fntype_abi (const_tree fntype)
>    if (lookup_attribute ("aarch64_vector_pcs", TYPE_ATTRIBUTES (fntype)))
>      return aarch64_simd_abi ();
>
> +  if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (fntype)))
> +    return aarch64_preserve_none_abi ();
> +
>    if (aarch64_returns_value_in_sve_regs_p (fntype)
>        || aarch64_takes_arguments_in_sve_regs_p (fntype))
>      return aarch64_sve_abi ();
> @@ -2519,6 +2554,10 @@ aarch64_reg_save_mode (unsigned int regno)
>    if (FP_REGNUM_P (regno))
>      switch (crtl->abi->id ())
>        {
> +      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;
> @@ -2649,7 +2688,9 @@ aarch64_hard_regno_call_part_clobbered (unsigned int 
> abi_id,
>                                         unsigned int regno,
>                                         machine_mode mode)
>  {
> -  if (FP_REGNUM_P (regno) && abi_id != ARM_PCS_SVE)
> +  if (FP_REGNUM_P (regno)
> +      && abi_id != ARM_PCS_SVE
> +      && abi_id != ARM_PCS_PRESERVE_NONE)
>      {
>        poly_int64 per_register_size = GET_MODE_SIZE (mode);
>        unsigned int nregs = hard_regno_nregs (regno, mode);
> @@ -6826,6 +6867,10 @@ aarch64_function_ok_for_sibcall (tree, tree exp)
>    auto from_abi = crtl->abi->id ();
>    auto to_abi = expr_callee_abi (exp).id ();
>
> +  /* preserve_none functions can tail-call anything that the base PCS can.  
> */
> +  if (from_abi != to_abi && from_abi == ARM_PCS_PRESERVE_NONE)
> +    from_abi = ARM_PCS_AAPCS64;
> +
>    /* ARM_PCS_SVE preserves strictly more than ARM_PCS_SIMD, which in
>       turn preserves strictly more than the base PCS.  The callee must
>       preserve everything that the caller is required to preserve.  */
> @@ -7287,6 +7332,49 @@ bitint_or_aggr_of_bitint_p (tree type)
>    return false;
>  }
>
> +/* How many GPR are available for argument passing in the procedure call
> +   standard.  */
> +static int
> +num_pcs_arg_regs (enum arm_pcs pcs)
> +{
> +  switch (pcs)
> +    {
> +    case ARM_PCS_PRESERVE_NONE:
> +      return NUM_PRESERVE_NONE_ARG_REGS;
> +    case ARM_PCS_AAPCS64:
> +    case ARM_PCS_SIMD:
> +    case ARM_PCS_SVE:
> +    case ARM_PCS_TLSDESC:
> +    case ARM_PCS_UNKNOWN:
> +      return NUM_ARG_REGS;
> +    }
> +  gcc_unreachable ();
> +}
> +
> +/* Get the NUM'th GPR argument passing register from the PCS procedure call
> + * standard.  */
> +
> +static int
> +get_pcs_arg_reg (enum arm_pcs pcs, int num)
> +{
> +  static const int ARM_PCS_PRESERVE_NONE_REGISTERS[] = 
> PRESERVE_NONE_REGISTERS;
> +
> +  gcc_assert (num < num_pcs_arg_regs (pcs));
> +
> +  switch (pcs)
> +    {
> +    case ARM_PCS_PRESERVE_NONE:
> +      return ARM_PCS_PRESERVE_NONE_REGISTERS[num];
> +    case ARM_PCS_AAPCS64:
> +    case ARM_PCS_SIMD:
> +    case ARM_PCS_SVE:
> +    case ARM_PCS_TLSDESC:
> +    case ARM_PCS_UNKNOWN:
> +      return R0_REGNUM + num;
> +    }
> +  gcc_unreachable ();
> +}
> +
>  /* 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
> @@ -7385,7 +7473,9 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
> function_arg_info &arg)
>          unprototyped function.  There is no ABI-defined location we
>          can return in this case, so we have no real choice but to raise
>          an error immediately, even though this is only a query function.  */
> -      if (arg.named && pcum->pcs_variant != ARM_PCS_SVE)
> +      if (arg.named
> +         && pcum->pcs_variant != ARM_PCS_SVE
> +         && pcum->pcs_variant != ARM_PCS_PRESERVE_NONE)
>         {
>           gcc_assert (!pcum->silent_p);
>           error ("SVE type %qT cannot be passed to an unprototyped function",
> @@ -7400,7 +7490,6 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
> function_arg_info &arg)
>        pcum->aapcs_nextnvrn = pcum->aapcs_nvrn + pst_info.num_zr ();
>        pcum->aapcs_nextnprn = pcum->aapcs_nprn + pst_info.num_pr ();
>        gcc_assert (arg.named
> -                 && pcum->pcs_variant == ARM_PCS_SVE
>                   && pcum->aapcs_nextnvrn <= NUM_FP_ARG_REGS
>                   && pcum->aapcs_nextnprn <= NUM_PR_ARG_REGS);
>        pcum->aapcs_reg = pst_info.get_rtx (mode, V0_REGNUM + pcum->aapcs_nvrn,
> @@ -7514,7 +7603,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
> function_arg_info &arg)
>    /* C6 - C9.  though the sign and zero extension semantics are
>       handled elsewhere.  This is the case where the argument fits
>       entirely general registers.  */
> -  if (allocate_ncrn && (ncrn + nregs <= NUM_ARG_REGS))
> +  if (allocate_ncrn && (ncrn + nregs <= num_pcs_arg_regs 
> (pcum->pcs_variant)))
>      {
>        gcc_assert (nregs == 0 || nregs == 1 || nregs == 2);
>
> @@ -7550,7 +7639,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
> function_arg_info &arg)
>                 inform (input_location, "parameter passing for argument of 
> type "
>                         "%qT changed in GCC 9.1", type);
>               ++ncrn;
> -             gcc_assert (ncrn + nregs <= NUM_ARG_REGS);
> +             gcc_assert (ncrn + nregs <= num_pcs_arg_regs 
> (pcum->pcs_variant));
>             }
>         }
>
> @@ -7572,7 +7661,8 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
> function_arg_info &arg)
>        if (nregs == 0
>           || (nregs == 1 && !sve_p)
>           || GET_MODE_CLASS (mode) == MODE_INT)
> -       pcum->aapcs_reg = gen_rtx_REG (mode, R0_REGNUM + ncrn);
> +       pcum->aapcs_reg
> +         = gen_rtx_REG (mode, get_pcs_arg_reg (pcum->pcs_variant, ncrn));
>        else
>         {
>           rtx par;
> @@ -7584,7 +7674,8 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
> function_arg_info &arg)
>               scalar_int_mode reg_mode = word_mode;
>               if (nregs == 1)
>                 reg_mode = int_mode_for_mode (mode).require ();
> -             rtx tmp = gen_rtx_REG (reg_mode, R0_REGNUM + ncrn + i);
> +             int reg = get_pcs_arg_reg (pcum->pcs_variant, ncrn + i);
> +             rtx tmp = gen_rtx_REG (reg_mode, reg);
>               tmp = gen_rtx_EXPR_LIST (VOIDmode, tmp,
>                                        GEN_INT (i * UNITS_PER_WORD));
>               XVECEXP (par, 0, i) = tmp;
> @@ -7597,7 +7688,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
> function_arg_info &arg)
>      }
>
>    /* C.11  */
> -  pcum->aapcs_nextncrn = NUM_ARG_REGS;
> +  pcum->aapcs_nextncrn = num_pcs_arg_regs (pcum->pcs_variant);
>
>    /* The argument is passed on stack; record the needed number of words for
>       this argument and align the total size if necessary.  */
> @@ -7675,7 +7766,8 @@ 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_SIMD
> -             || pcum->pcs_variant == ARM_PCS_SVE);
> +             || pcum->pcs_variant == ARM_PCS_SVE
> +             || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE);
>
>    if (arg.end_marker_p ())
>      {
> @@ -7767,7 +7859,8 @@ aarch64_function_arg_advance (cumulative_args_t pcum_v,
>    CUMULATIVE_ARGS *pcum = get_cumulative_args (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_SVE
> +      || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE)
>      {
>        aarch64_layout_arg (pcum_v, arg);
>        gcc_assert ((pcum->aapcs_reg != NULL_RTX)
> @@ -7786,13 +7879,41 @@ aarch64_function_arg_advance (cumulative_args_t 
> pcum_v,
>      }
>  }
>
> -bool
> -aarch64_function_arg_regno_p (unsigned regno)
> +/* Checks if a register is live at entry of a preserve_none pcs function.
> +   That is, it used for passing registers.  See 
> ARM_PCS_PRESERVE_NONE_REGISTERS
> +   for full list and order of argument passing registers.  */
> +
> +static bool
> +function_arg_preserve_none_regno_p (unsigned regno)
>  {
> -  return ((GP_REGNUM_P (regno) && regno < R0_REGNUM + NUM_ARG_REGS)
> +  return ((GP_REGNUM_P (regno) && regno != R8_REGNUM && regno != R15_REGNUM
> +          && regno != R16_REGNUM && regno != R17_REGNUM && regno != 
> R18_REGNUM
> +          && regno != R19_REGNUM && regno != R29_REGNUM && regno != 
> R30_REGNUM)
>           || (FP_REGNUM_P (regno) && regno < V0_REGNUM + NUM_FP_ARG_REGS)
>           || (PR_REGNUM_P (regno) && regno < P0_REGNUM + NUM_PR_ARG_REGS));
>  }
> +/* Implements FUNCTION_ARG_REGNO_P.  */
> +bool
> +aarch64_function_arg_regno_p (unsigned regno)
> +{
> +  enum arm_pcs pcs
> +    = cfun ? (arm_pcs) fndecl_abi (cfun->decl).id () : ARM_PCS_AAPCS64;
> +
> +  switch (pcs)
> +    {
> +    case ARM_PCS_AAPCS64:
> +    case ARM_PCS_SIMD:
> +    case ARM_PCS_SVE:
> +    case ARM_PCS_TLSDESC:
> +    case ARM_PCS_UNKNOWN:
> +      return ((GP_REGNUM_P (regno) && regno < R0_REGNUM + NUM_ARG_REGS)
> +             || (FP_REGNUM_P (regno) && regno < V0_REGNUM + NUM_FP_ARG_REGS)
> +             || (PR_REGNUM_P (regno) && regno < P0_REGNUM + 
> NUM_PR_ARG_REGS));
> +    case ARM_PCS_PRESERVE_NONE:
> +      return function_arg_preserve_none_regno_p (regno);
> +    }
> +  gcc_unreachable ();
> +}
>
>  /* Implement FUNCTION_ARG_BOUNDARY.  Every parameter gets at least
>     PARM_BOUNDARY bits of alignment, but will be given anything up
> @@ -21804,8 +21925,9 @@ aarch64_expand_builtin_va_start (tree valist, rtx 
> nextarg ATTRIBUTE_UNUSED)
>
>    cum = &crtl->args.info;
>    if (cfun->va_list_gpr_size)
> -    gr_save_area_size = MIN ((NUM_ARG_REGS - cum->aapcs_ncrn) * 
> UNITS_PER_WORD,
> -                            cfun->va_list_gpr_size);
> +    gr_save_area_size = MIN ((num_pcs_arg_regs (cum->pcs_variant)
> +                             - cum->aapcs_ncrn)
> +                            * UNITS_PER_WORD, cfun->va_list_gpr_size);
>    if (cfun->va_list_fpr_size)
>      vr_save_area_size = MIN ((NUM_FP_ARG_REGS - cum->aapcs_nvrn)
>                              * UNITS_PER_VREG, cfun->va_list_fpr_size);
> @@ -22190,7 +22312,8 @@ aarch64_setup_incoming_varargs (cumulative_args_t 
> cum_v,
>    /* Found out how many registers we need to save.
>       Honor tree-stdvar analysis results.  */
>    if (cfun->va_list_gpr_size)
> -    gr_saved = MIN (NUM_ARG_REGS - local_cum.aapcs_ncrn,
> +    gr_saved = MIN (num_pcs_arg_regs (local_cum.pcs_variant)
> +                   - local_cum.aapcs_ncrn,
>                     cfun->va_list_gpr_size / UNITS_PER_WORD);
>    if (cfun->va_list_fpr_size)
>      vr_saved = MIN (NUM_FP_ARG_REGS - local_cum.aapcs_nvrn,
> @@ -22214,8 +22337,22 @@ aarch64_setup_incoming_varargs (cumulative_args_t 
> cum_v,
>           mem = gen_frame_mem (BLKmode, ptr);
>           set_mem_alias_set (mem, get_varargs_alias_set ());
>
> -         move_block_from_reg (local_cum.aapcs_ncrn + R0_REGNUM,
> -                              mem, gr_saved);
> +         /* For preserve_none pcs we can't use move_block_from_reg as the
> +            argument passing register order is not consecutive.  */
> +         if (local_cum.pcs_variant == ARM_PCS_PRESERVE_NONE)
> +           {
> +             for (int i = 0; i < gr_saved; ++i)
> +               {
> +                 rtx tem = operand_subword (mem, i, 1, BLKmode);
> +                 gcc_assert (tem);
> +                 int reg = get_pcs_arg_reg (local_cum.pcs_variant,
> +                                            local_cum.aapcs_ncrn + i);
> +                 emit_move_insn (tem, gen_rtx_REG (word_mode, reg));
> +               }
> +           }
> +         else
> +           move_block_from_reg (R0_REGNUM + local_cum.aapcs_ncrn, mem,
> +                                gr_saved);
>         }
>        if (vr_saved > 0)
>         {
> @@ -25521,7 +25658,7 @@ aarch64_is_variant_pcs (tree fndecl)
>  {
>    /* Check for ABIs that preserve more registers than usual.  */
>    arm_pcs pcs = (arm_pcs) fndecl_abi (fndecl).id ();
> -  if (pcs == ARM_PCS_SIMD || pcs == ARM_PCS_SVE)
> +  if (pcs == ARM_PCS_SIMD || pcs == ARM_PCS_SVE || pcs == 
> ARM_PCS_PRESERVE_NONE)
>      return true;
>
>    /* Check for ABIs that allow PSTATE.SM to be 1 on entry.  */
> @@ -30252,6 +30389,8 @@ aarch64_comp_type_attributes (const_tree type1, 
> const_tree type2)
>
>    if (!check_attr ("gnu", "aarch64_vector_pcs"))
>      return 0;
> +  if (!check_attr ("gnu", "preserve_none"))
> +    return 0;
>    if (!check_attr ("gnu", "indirect_return"))
>      return 0;
>    if (!check_attr ("gnu", "Advanced SIMD type"))
> diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
> index 2cd929d83f9..0e596b59744 100644
> --- a/gcc/config/aarch64/aarch64.h
> +++ b/gcc/config/aarch64/aarch64.h
> @@ -696,6 +696,31 @@ through +ssve-fp8dot2.  */
>  #define NUM_FP_ARG_REGS                        8
>  #define NUM_PR_ARG_REGS                        4
>
> +/* The argument passing regs for preserve_none pcs.  */
> +#if TARGET_AARCH64_MS_ABI
> +#define NUM_PRESERVE_NONE_ARG_REGS 23
> +#define PRESERVE_NONE_REGISTERS \
> +{ \
> +  R20_REGNUM, R21_REGNUM, R22_REGNUM, R23_REGNUM, R24_REGNUM, R25_REGNUM,\
> +  R26_REGNUM, R27_REGNUM, R28_REGNUM,\
> +  R0_REGNUM, R1_REGNUM, R2_REGNUM, R3_REGNUM, R4_REGNUM, R5_REGNUM,\
> +  R6_REGNUM, R7_REGNUM,\
> +  R10_REGNUM, R11_REGNUM, R12_REGNUM, R13_REGNUM, R14_REGNUM, R9_REGNUM\
> +}
> +#else
> +#define NUM_PRESERVE_NONE_ARG_REGS 24
> +#define PRESERVE_NONE_REGISTERS \
> +{ \
> +  R20_REGNUM, R21_REGNUM, R22_REGNUM, R23_REGNUM, R24_REGNUM, R25_REGNUM,\
> +  R26_REGNUM, R27_REGNUM, R28_REGNUM,\
> +  R0_REGNUM, R1_REGNUM, R2_REGNUM, R3_REGNUM, R4_REGNUM, R5_REGNUM,\
> +  R6_REGNUM, R7_REGNUM,\
> +  R10_REGNUM, R11_REGNUM, R12_REGNUM, R13_REGNUM, R14_REGNUM, R9_REGNUM,\
> +  R15_REGNUM\
> +}
> +#endif
> +
> +
>  /* A Homogeneous Floating-Point or Short-Vector Aggregate may have at most
>     four members.  */
>  #define HA_MAX_NUM_FLDS                4
> @@ -1150,6 +1175,8 @@ enum arm_pcs
>    ARM_PCS_SVE,                 /* For functions that pass or return
>                                    values in SVE registers.  */
>    ARM_PCS_TLSDESC,             /* For targets of tlsdesc calls.  */
> +  ARM_PCS_PRESERVE_NONE,       /* PCS variant with no call-preserved
> +                                  registers except X29.  */
>    ARM_PCS_UNKNOWN
>  };
>
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index fb117f59665..4e906c2d7a2 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -3930,6 +3930,27 @@ threads, such as the POSIX @code{swapcontext} 
> function.  This attribute
>  adds a @code{BTI J} instruction when BTI is enabled e.g. via
>  @option{-mbranch-protection}.
>
> +@cindex @code{preserve_none} function attribute, AArch64
> +@item preserve_none
> +Use this attribute to change the procedure call standard of the specified
> +function to the preserve-none variant.
> +
> +The preserve-none ABI variant modifies the AAPCS such that has no 
> callee-saved
> +registers (including SIMD and floating-point registers). That is, with the
> +exception of the stack register, link register (r30), and frame pointer 
> (r29),
> +all registers are caller saved, and can be used as scratch registers by the
> +callee.
> +
> +Additionally, registers r20--r28, r0--r7, r10--r14, r9 and r15 are used for
> +argument passing, in that order. For Microsoft Windows targets
> +r15 is not used for argument passing.
> +
> +The return value registers remain r0 and r1 in both cases.
> +
> +All other details are the same as for the AAPCS ABI.
> +
> +This ABI has not been stabilized, and may be subject to change in future
> +versions.
>  @end table
>
>  The above target attributes can be specified as follows:
> diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_1.c 
> b/gcc/testsuite/gcc.target/aarch64/preserve_none_1.c
> new file mode 100644
> index 00000000000..1390173836d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_1.c
> @@ -0,0 +1,143 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-schedule-insns2" } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +/* { dg-skip-if "" { *-*-mingw* } } */
> +
> +void normal_callee();
> +void preserve_none_callee() [[gnu::preserve_none]];
> +
> +#pragma GCC target "+sve"
> +
> +/*
> +** preserve_none_caller1:
> +** ?#APP
> +**     nop
> +** ?#NO_APP
> +**     ret
> +*/
> +void preserve_none_caller1() [[gnu::preserve_none]]
> +{
> +  asm volatile ("nop" ::: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
> +               "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
> +               "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
> +               "x24", "x25", "x26", "x27", "x28",
> +
> +               "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7",
> +               "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15",
> +               "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23",
> +               "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31",
> +
> +               "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7",
> +               "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15");
> +}
> +
> +/*
> +** preserve_none_caller2:
> +**     stp     x29, x30, \[sp, #?-16\]!
> +**     mov     x29, sp
> +**     bl      normal_callee
> +**     mov     w0, w20
> +**     ldp     x29, x30, \[sp\], #?16
> +**     ret
> +*/
> +int preserve_none_caller2(int x) [[gnu::preserve_none]]
> +{
> +  normal_callee();
> +  return x;
> +}
> +
> +/*
> +** preserve_none_caller3:
> +**     stp     x29, x30, \[sp, #?-32\]!
> +**     mov     x29, sp
> +**     str     w20, \[sp, #?[0-9]+\]
> +**     bl      preserve_none_callee
> +**     ldr     w0, \[sp, #?[0-9]+\]
> +**     ldp     x29, x30, \[sp\], #?32
> +**     ret
> +*/
> +int preserve_none_caller3(int x) [[gnu::preserve_none]]
> +{
> +  preserve_none_callee();
> +  return x;
> +}
> +
> +/*
> +** preserve_none_caller4:
> +**     b       preserve_none_callee
> +*/
> +void preserve_none_caller4() [[gnu::preserve_none]]
> +{
> +  preserve_none_callee();
> +}
> +
> +/*
> +** preserve_none_caller5:
> +**     b       preserve_none_callee
> +*/
> +void preserve_none_caller5(__SVBool_t x) [[gnu::preserve_none]]
> +{
> +  preserve_none_callee();
> +}
> +
> +/*
> +** normal_caller1:
> +**     stp     x29, x30, \[sp, #?-160\]!
> +**     mov     x29, sp
> +**     stp     x19, x20, \[sp, #?16\]
> +**     stp     x21, x22, \[sp, #?32\]
> +**     stp     x23, x24, \[sp, #?48\]
> +**     stp     x25, x26, \[sp, #?64\]
> +**     stp     x27, x28, \[sp, #?80\]
> +**     stp     d8, d9, \[sp, #?96\]
> +**     stp     d10, d11, \[sp, #?112\]
> +**     stp     d12, d13, \[sp, #?128\]
> +**     stp     d14, d15, \[sp, #?144\]
> +**     bl      preserve_none_callee
> +**     ldp     d8, d9, \[sp, #?96\]
> +**     ldp     d10, d11, \[sp, #?112\]
> +**     ldp     d12, d13, \[sp, #?128\]
> +**     ldp     d14, d15, \[sp, #?144\]
> +**     ldp     x19, x20, \[sp, #?16\]
> +**     ldp     x21, x22, \[sp, #?32\]
> +**     ldp     x23, x24, \[sp, #?48\]
> +**     ldp     x25, x26, \[sp, #?64\]
> +**     ldp     x27, x28, \[sp, #?80\]
> +**     ldp     x29, x30, \[sp\], #?160
> +**     ret
> +*/
> +void normal_caller1()
> +{
> +  preserve_none_callee();
> +}
> +
> +/*
> +** normal_caller2:
> +**     stp     x29, x30, \[sp, #?-160\]!
> +**     mov     x29, sp
> +**     stp     x19, x20, \[sp, #?16\]
> +**     stp     x21, x22, \[sp, #?32\]
> +**     stp     x23, x24, \[sp, #?48\]
> +**     stp     x25, x26, \[sp, #?64\]
> +**     stp     x27, x28, \[sp, #?80\]
> +**     stp     d8, d9, \[sp, #?96\]
> +**     stp     d10, d11, \[sp, #?112\]
> +**     stp     d12, d13, \[sp, #?128\]
> +**     stp     d14, d15, \[sp, #?144\]
> +**     blr     x0
> +**     ldp     d8, d9, \[sp, #?96\]
> +**     ldp     d10, d11, \[sp, #?112\]
> +**     ldp     d12, d13, \[sp, #?128\]
> +**     ldp     d14, d15, \[sp, #?144\]
> +**     ldp     x19, x20, \[sp, #?16\]
> +**     ldp     x21, x22, \[sp, #?32\]
> +**     ldp     x23, x24, \[sp, #?48\]
> +**     ldp     x25, x26, \[sp, #?64\]
> +**     ldp     x27, x28, \[sp, #?80\]
> +**     ldp     x29, x30, \[sp\], #?160
> +**     ret
> +*/
> +void normal_caller2(void (*callee)() [[gnu::preserve_none]])
> +{
> +  callee();
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_2.c 
> b/gcc/testsuite/gcc.target/aarch64/preserve_none_2.c
> new file mode 100644
> index 00000000000..1bb89e026e5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_2.c
> @@ -0,0 +1,49 @@
> +/* { dg-options "" } */
> +
> +void multi1() [[gnu::aarch64_vector_pcs, gnu::preserve_none]]; /* { 
> dg-warning {ignoring attribute 'preserve_none' because it conflicts} } */
> +void multi2() [[gnu::preserve_none, gnu::aarch64_vector_pcs]]; /* { 
> dg-warning {ignoring attribute 'aarch64_vector_pcs' because it conflicts} } */
> +
> +void normal_callee();
> +void preserve_none_callee() [[gnu::preserve_none]];
> +void vector_callee() [[gnu::aarch64_vector_pcs]];
> +void sve_callee(__SVBool_t);
> +void sve_preserve_none_callee(__SVBool_t) [[gnu::preserve_none]];
> +
> +void (*normal_ptr)();
> +void (*preserve_none_ptr)() [[gnu::preserve_none]];
> +void (*vector_ptr)() [[gnu::aarch64_vector_pcs]];
> +void (*sve_ptr)(__SVBool_t);
> +void (*sve_preserve_none_ptr)(__SVBool_t) [[gnu::preserve_none]];
> +
> +void f()
> +{
> +  normal_ptr = normal_callee;
> +  normal_ptr = preserve_none_callee; /* { dg-error {incompatible pointer 
> type} } */
> +  normal_ptr = vector_callee; /* { dg-error {incompatible pointer type} } */
> +  normal_ptr = sve_callee; /* { dg-error {incompatible pointer type} } */
> +  normal_ptr = sve_preserve_none_callee; /* { dg-error {incompatible pointer 
> type} } */
> +
> +  preserve_none_ptr = normal_callee; /* { dg-error {incompatible pointer 
> type} } */
> +  preserve_none_ptr = preserve_none_callee;
> +  preserve_none_ptr = vector_callee; /* { dg-error {incompatible pointer 
> type} } */
> +  preserve_none_ptr = sve_callee; /* { dg-error {incompatible pointer type} 
> } */
> +  preserve_none_ptr = sve_preserve_none_callee; /* { dg-error {incompatible 
> pointer type} } */
> +
> +  vector_ptr = normal_callee; /* { dg-error {incompatible pointer type} } */
> +  vector_ptr = preserve_none_callee; /* { dg-error {incompatible pointer 
> type} } */
> +  vector_ptr = vector_callee;
> +  vector_ptr = sve_callee; /* { dg-error {incompatible pointer type} } */
> +  vector_ptr = sve_preserve_none_callee; /* { dg-error {incompatible pointer 
> type} } */
> +
> +  sve_ptr = normal_callee; /* { dg-error {incompatible pointer type} } */
> +  sve_ptr = preserve_none_callee; /* { dg-error {incompatible pointer type} 
> } */
> +  sve_ptr = vector_callee; /* { dg-error {incompatible pointer type} } */
> +  sve_ptr = sve_callee;
> +  sve_ptr = sve_preserve_none_callee; /* { dg-error {incompatible pointer 
> type} } */
> +
> +  sve_preserve_none_ptr = normal_callee; /* { dg-error {incompatible pointer 
> type} } */
> +  sve_preserve_none_ptr = preserve_none_callee; /* { dg-error {incompatible 
> pointer type} } */
> +  sve_preserve_none_ptr = vector_callee; /* { dg-error {incompatible pointer 
> type} } */
> +  sve_preserve_none_ptr = sve_callee; /* { dg-error {incompatible pointer 
> type} } */
> +  sve_preserve_none_ptr = sve_preserve_none_callee;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_3.c 
> b/gcc/testsuite/gcc.target/aarch64/preserve_none_3.c
> new file mode 100644
> index 00000000000..0258177f422
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_3.c
> @@ -0,0 +1,114 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -std=gnu23" } */
> +
> +int no_arg_stack_use_callee
> +  [[gnu::preserve_none, gnu::noinline,
> +    gnu::noipa]] (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
> +                 int a7, int a8, int a9, int a10, int a11, int a12, int a13,
> +                 int a14, int a15, int a16, int a17, int a18, int a19, int 
> a20,
> +                 int a21, int a22, int a23)
> +{
> +  /* Clobber all the registers to check they are correctly marked live at the
> +     start.  */
> +  asm volatile("mov x0, #0;"
> +              "mov x1, #0;"
> +              "mov x2, #0;"
> +              "mov x3, #0;"
> +              "mov x4, #0;"
> +              "mov x5, #0;"
> +              "mov x6, #0;"
> +              "mov x7, #0;"
> +              "mov x8, #0;"
> +              "mov x9, #0;"
> +              "mov x10, #0;"
> +              "mov x11, #0;"
> +              "mov x12, #0;"
> +              "mov x13, #0;"
> +              "mov x14, #0;"
> +              "mov x15, #0;"
> +              "mov x16, #0;"
> +              "mov x17, #0;"
> +              "mov x18, #0;"
> +              "mov x19, #0;"
> +              "mov x20, #0;"
> +              "mov x21, #0;"
> +              "mov x22, #0;"
> +              "mov x23, #0;"
> +              "mov x24, #0;"
> +              "mov x25, #0;"
> +              "mov x26, #0;"
> +              "mov x27, #0;"
> +              "mov x28, #0;" ::
> +                : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9",
> +                  "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17",
> +                  "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25",
> +                  "x26", "x27", "x28");
> +
> +  return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + 
> a13
> +        + a14 + a15 + a16 + a17 + a18 + a19 + a20 + a21 + a22 + a23;
> +}
> +
> +int arg_stack_use_callee
> +  [[gnu::preserve_none, gnu::noinline,
> +    gnu::noipa]] (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
> +                 int a7, int a8, int a9, int a10, int a11, int a12, int a13,
> +                 int a14, int a15, int a16, int a17, int a18, int a19, int 
> a20,
> +                 int a21, int a22, int a23, int a24)
> +{
> +  /* Clobber all the registers to check they are correctly marked live at the
> +     start.  */
> +  asm volatile("mov x0, #0;"
> +              "mov x1, #0;"
> +              "mov x2, #0;"
> +              "mov x3, #0;"
> +              "mov x4, #0;"
> +              "mov x5, #0;"
> +              "mov x6, #0;"
> +              "mov x7, #0;"
> +              "mov x8, #0;"
> +              "mov x9, #0;"
> +              "mov x10, #0;"
> +              "mov x11, #0;"
> +              "mov x12, #0;"
> +              "mov x13, #0;"
> +              "mov x14, #0;"
> +              "mov x15, #0;"
> +              "mov x16, #0;"
> +              "mov x17, #0;"
> +              "mov x18, #0;"
> +              "mov x19, #0;"
> +              "mov x20, #0;"
> +              "mov x21, #0;"
> +              "mov x22, #0;"
> +              "mov x23, #0;"
> +              "mov x24, #0;"
> +              "mov x25, #0;"
> +              "mov x26, #0;"
> +              "mov x27, #0;"
> +              "mov x28, #0;" ::
> +                : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9",
> +                  "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17",
> +                  "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25",
> +                  "x26", "x27", "x28");
> +
> +  return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + 
> a13
> +        + a14 + a15 + a16 + a17 + a18 + a19 + a20 + a21 + a22 + a23 + a24;
> +}
> +
> +int
> +main ()
> +{
> +  int res = no_arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
> 12,
> +                                    13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 
> 23);
> +
> +  if (res != 23 * 24 / 2)
> +    return 1;
> +
> +  res = arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 
> 14,
> +                             15, 16, 17, 18, 19, 20, 21, 22, 23, 24);
> +
> +  if (res != 24 * 25 / 2)
> +    return 1;
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_4.c 
> b/gcc/testsuite/gcc.target/aarch64/preserve_none_4.c
> new file mode 100644
> index 00000000000..fc8347d6c28
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_4.c
> @@ -0,0 +1,99 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-schedule-insns2" } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +/* { dg-skip-if "" { *-*-mingw* } } */
> +
> +int no_arg_stack_use_callee
> +  [[gnu::preserve_none, gnu::noinline,
> +    gnu::noipa]] (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
> +                 int a7, int a8, int a9, int a10, int a11, int a12, int a13,
> +                 int a14, int a15, int a16, int a17, int a18, int a19, int 
> a20,
> +                 int a21, int a22, int a24);
> +
> +/* Check the pcs argument order is correct. Should be x20-28, x0-7, x10-14, 
> x9,
> + * x15 and that the return arg is x0 */
> +
> +/*
> +** no_arg_stack_use_caller:
> +** ...
> +**     mov     w15, 23
> +**     mov     w9, 22
> +**     mov     w14, 21
> +**     mov     w13, 20
> +**     mov     w12, 19
> +**     mov     w11, 18
> +**     mov     w10, 17
> +**     mov     w7, 16
> +**     mov     w6, 15
> +**     mov     w5, 14
> +**     mov     w4, 13
> +**     mov     w3, 12
> +**     mov     w2, 11
> +**     mov     w1, 10
> +**     mov     w0, 9
> +**     mov     w28, 8
> +**     mov     w27, 7
> +**     mov     w26, 6
> +**     mov     w25, 5
> +**     mov     w24, 4
> +**     mov     w23, 3
> +**     mov     w22, 2
> +**     mov     w21, 1
> +**     mov     w20, 0
> +**     bl      no_arg_stack_use_callee
> +**     add     w0, w0, 1
> +** ...
> +*/
> +int no_arg_stack_use_caller [[gnu::preserve_none]] ()
> +{
> +  return no_arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
> 13,
> +                                 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
> +        + 1;
> +}
> +
> +int arg_stack_use_callee
> +  [[gnu::preserve_none, gnu::noinline,
> +    gnu::noipa]] (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
> +                 int a7, int a8, int a9, int a10, int a11, int a12, int a13,
> +                 int a14, int a15, int a16, int a17, int a18, int a19, int 
> a20,
> +                 int a21, int a22, int a23, int a24);
> +
> +/*
> +** arg_stack_use_caller:
> +** ...
> +**     mov     w0, 24
> +**     mov     w15, 23
> +**     mov     w9, 22
> +**     mov     w14, 21
> +**     mov     w13, 20
> +**     mov     w12, 19
> +**     mov     w11, 18
> +**     mov     w10, 17
> +**     mov     w7, 16
> +**     mov     w6, 15
> +**     mov     w5, 14
> +**     mov     w4, 13
> +**     mov     w3, 12
> +**     mov     w2, 11
> +**     mov     w1, 10
> +**     mov     w28, 8
> +**     mov     w27, 7
> +**     mov     w26, 6
> +**     mov     w25, 5
> +**     mov     w24, 4
> +**     mov     w23, 3
> +**     mov     w22, 2
> +**     mov     w21, 1
> +**     mov     w20, 0
> +**     str     w0, \[sp\]
> +**     mov     w0, 9
> +**     bl      arg_stack_use_callee
> +**     add     w0, w0, 1
> +** ...
> +*/
> +int arg_stack_use_caller [[gnu::preserve_none]] ()
> +{
> +  return arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 
> 14,
> +                              15, 16, 17, 18, 19, 20, 21, 22, 23, 24)
> +        + 1;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_5.c 
> b/gcc/testsuite/gcc.target/aarch64/preserve_none_5.c
> new file mode 100644
> index 00000000000..d11bf10a898
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_5.c
> @@ -0,0 +1,47 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-schedule-insns2" } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +/* { dg-skip-if "" { *-*-mingw* } } */
> +
> +#include <stdarg.h>
> +int foo [[gnu::preserve_none]] (...);
> +
> +/* Check the pcs argument order is correct. Should be x20-28, x0-7, x10-14, 
> x9, x15 and that the return arg is x0 */
> +
> +/*
> +** bar:
> +** ...
> +**     mov     w15, 23
> +**     mov     w9, 22
> +**     mov     w14, 21
> +**     mov     w13, 20
> +**     mov     w12, 19
> +**     mov     w11, 18
> +**     mov     w10, 17
> +**     mov     w7, 16
> +**     mov     w6, 15
> +**     mov     w5, 14
> +**     mov     w4, 13
> +**     mov     w3, 12
> +**     mov     w2, 11
> +**     mov     w1, 10
> +**     mov     w0, 9
> +**     mov     w28, 8
> +**     mov     w27, 7
> +**     mov     w26, 6
> +**     mov     w25, 5
> +**     mov     w24, 4
> +**     mov     w23, 3
> +**     mov     w22, 2
> +**     mov     w21, 1
> +**     mov     w20, 0
> +**     bl      foo
> +**     add     w0, w0, 1
> +** ...
> +*/
> +int bar [[gnu::preserve_none]] ()
> +{
> +  return foo (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 
> 18,
> +             19, 20, 21, 22, 23)
> +        + 1;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_6.c 
> b/gcc/testsuite/gcc.target/aarch64/preserve_none_6.c
> new file mode 100644
> index 00000000000..687e30a063e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_6.c
> @@ -0,0 +1,76 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -std=gnu23" } */
> +
> +#include <stdarg.h>
> +#include <stdio.h>
> +
> +int preserve_none_va_func
> +  [[gnu::preserve_none, gnu::noinline, gnu::noclone]] (int count, ...)
> +{
> +  asm volatile("mov x0, #0;"
> +              "mov x1, #0;"
> +              "mov x2, #0;"
> +              "mov x3, #0;"
> +              "mov x4, #0;"
> +              "mov x5, #0;"
> +              "mov x6, #0;"
> +              "mov x7, #0;"
> +              "mov x8, #0;"
> +              "mov x9, #0;"
> +              "mov x10, #0;"
> +              "mov x11, #0;"
> +              "mov x12, #0;"
> +              "mov x13, #0;"
> +              "mov x14, #0;"
> +              "mov x15, #0;"
> +              "mov x16, #0;"
> +              "mov x17, #0;"
> +              "mov x18, #0;"
> +              "mov x19, #0;"
> +              "mov x20, #0;"
> +              "mov x21, #0;"
> +              "mov x22, #0;"
> +              "mov x23, #0;"
> +              "mov x24, #0;"
> +              "mov x25, #0;"
> +              "mov x26, #0;"
> +              "mov x27, #0;"
> +              "mov x28, #0;" ::
> +                : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9",
> +                  "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17",
> +                  "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25",
> +                  "x26", "x27", "x28");
> +
> +  int sum = 0;
> +
> +  va_list args;
> +
> +  va_start (args, count);
> +  for (int i = 0; i < count; i++)
> +    sum += va_arg (args, int);
> +  va_end (args);
> +
> +  return sum;
> +}
> +
> +int
> +main ()
> +{
> +  int res = preserve_none_va_func (23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
> 12,
> +                                  13, 14, 15, 16, 17, 18, 19, 20, 21, 22);
> +  if (res != 23 * 22 / 2)
> +    return 1;
> +
> +  res = preserve_none_va_func (24, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
> 13,
> +                              14, 15, 16, 17, 18, 19, 20, 21, 22, 23);
> +
> +  if (res != 24 * 23 / 2)
> +    return 1;
> +
> +  res = preserve_none_va_func (25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
> 13,
> +                              14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24);
> +  if (res != 25 * 24 / 2)
> +    return 1;
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_mingw_1.c 
> b/gcc/testsuite/gcc.target/aarch64/preserve_none_mingw_1.c
> new file mode 100644
> index 00000000000..11703c8bb96
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_mingw_1.c
> @@ -0,0 +1,93 @@
> +/* { dg-do compile { target aarch64*-*-mingw* } } */
> +/* { dg-options "-O2 -fno-schedule-insns2" } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +int no_arg_stack_use_callee [[gnu::preserve_none, gnu::noinline, gnu::noipa]]
> +                       (int a0, int a1, int a2, int a3, int a4, int a5, int 
> a6,
> +                        int a7, int a8, int a9, int a10, int a11, int a12,
> +                        int a13, int a14, int a15, int a16, int a17, int a18,
> +                        int a19, int a20, int a21, int a22);
> +
> +/* Check the pcs argument order is correct. Should be x20-28, x0-7, x10-14, 
> x9, and that the return arg is x0 */
> +
> +/*
> +** no_arg_stack_use_caller:
> +** ...
> +**     mov     w9, 22
> +**     mov     w14, 21
> +**     mov     w13, 20
> +**     mov     w12, 19
> +**     mov     w11, 18
> +**     mov     w10, 17
> +**     mov     w7, 16
> +**     mov     w6, 15
> +**     mov     w5, 14
> +**     mov     w4, 13
> +**     mov     w3, 12
> +**     mov     w2, 11
> +**     mov     w1, 10
> +**     mov     w0, 9
> +**     mov     w28, 8
> +**     mov     w27, 7
> +**     mov     w26, 6
> +**     mov     w25, 5
> +**     mov     w24, 4
> +**     mov     w23, 3
> +**     mov     w22, 2
> +**     mov     w21, 1
> +**     mov     w20, 0
> +**     bl      no_arg_stack_use_callee
> +**     add     w0, w0, 1
> +** ...
> +*/
> +int no_arg_stack_use_caller [[gnu::preserve_none]] ()
> +{
> +  return no_arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
> 13,
> +                                 14, 15, 16, 17, 18, 19, 20, 21, 22)
> +        + 1;
> +}
> +
> +int arg_stack_use_callee [[gnu::preserve_none, gnu::noinline, gnu::noipa]]
> +                       (int a0, int a1, int a2, int a3, int a4, int a5, int 
> a6,
> +                        int a7, int a8, int a9, int a10, int a11, int a12,
> +                        int a13, int a14, int a15, int a16, int a17, int a18,
> +                        int a19, int a20, int a21, int a22, int a23);
> +
> +/*
> +** arg_stack_use_caller:
> +** ...
> +**     mov     w0, 23
> +**     mov     w9, 22
> +**     mov     w14, 21
> +**     mov     w13, 20
> +**     mov     w12, 19
> +**     mov     w11, 18
> +**     mov     w10, 17
> +**     mov     w7, 16
> +**     mov     w6, 15
> +**     mov     w5, 14
> +**     mov     w4, 13
> +**     mov     w3, 12
> +**     mov     w2, 11
> +**     mov     w1, 10
> +**     mov     w28, 8
> +**     mov     w27, 7
> +**     mov     w26, 6
> +**     mov     w25, 5
> +**     mov     w24, 4
> +**     mov     w23, 3
> +**     mov     w22, 2
> +**     mov     w21, 1
> +**     mov     w20, 0
> +**     str     w0, \[sp\]
> +**     mov     w0, 9
> +**     bl      arg_stack_use_callee
> +**     add     w0, w0, 1
> +** ...
> +*/
> +int arg_stack_use_caller [[gnu::preserve_none]] ()
> +{
> +  return arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 
> 14,
> +                              15, 16, 17, 18, 19, 20, 21, 22, 23)
> +        + 1;
> +}
> --
> 2.34.1
>

Reply via email to