Hi Alfie,

> -----Original Message-----
> From: Alfie Richards <[email protected]>
> Sent: 23 October 2025 14:31
> To: [email protected]
> Cc: Richard Earnshaw <[email protected]>; Tamar Christina
> <[email protected]>; [email protected]; Alice Carlotti
> <[email protected]>; Alex Coplan <[email protected]>; Wilco
> Dijkstra <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; Alfie Richards <[email protected]>
> Subject: [PATCH v2] aarch64: Add support for preserve_none function
> attribute [PR target/118328]
> 
> Hi all,
> 
> Updated the documentation part of this patch after some feedback.
> 
> Functional code unchanged.
> 
> Reg tested on AArch64.
> 
> Ok for master?
> 
> 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, 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_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                 | 178 ++++++++++++++++--
>  gcc/config/aarch64/aarch64.h                  |  13 ++
>  gcc/doc/extend.texi                           |  18 ++
>  .../gcc.target/aarch64/preserve_none_1.c      | 142 ++++++++++++++
>  .../gcc.target/aarch64/preserve_none_2.c      |  49 +++++
>  .../gcc.target/aarch64/preserve_none_3.c      | 109 +++++++++++
>  .../gcc.target/aarch64/preserve_none_4.c      |  93 +++++++++
>  .../gcc.target/aarch64/preserve_none_5.c      |  45 +++++
>  .../gcc.target/aarch64/preserve_none_6.c      |  66 +++++++
>  9 files changed, 693 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
> 
> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> index b86064148fe..5bab2807c2a 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,9 @@ aarch64_reg_save_mode (unsigned int regno)
>    if (FP_REGNUM_P (regno))
>      switch (crtl->abi->id ())
>        {
> +      case ARM_PCS_PRESERVE_NONE:
> +     /* We should never save FPRs for preserve_none, but nevertheless
> +        treat it like the base PCS for consistency.  */

This comment is a bit confusing to me as the documentation you added states
that FPRs are saved as callee saves by design.  So in that sense adding preserve
none here makes sense, but the comment is confusing.

>        case ARM_PCS_AAPCS64:
>       /* Only the low 64 bits are saved by the base PCS.  */
>       return DFmode;
> @@ -2649,7 +2687,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 +6866,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 +7331,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 +7472,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 +7489,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 +7602,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 +7638,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 +7660,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 +7673,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 +7687,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 +7765,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 +7858,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 +7878,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 +21924,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 +22311,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 +22336,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 +25657,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 +30388,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..79528696da0 100644
> --- a/gcc/config/aarch64/aarch64.h
> +++ b/gcc/config/aarch64/aarch64.h
> @@ -696,6 +696,17 @@ through +ssve-fp8dot2.  */
>  #define NUM_FP_ARG_REGS                      8
>  #define NUM_PR_ARG_REGS                      4
> 
> +/* The argument passing regs for preserve_none pcs.  */
> +#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\
> +}
> +

According to the LLVM documentation on this[1], R15 is also an argument passing
register on non-Windows.  So I think it should be here conditionally?

[1] https://clang.llvm.org/docs/AttributeReference.html#preserve-none

>  /* A Homogeneous Floating-Point or Short-Vector Aggregate may have at
> most
>     four members.  */
>  #define HA_MAX_NUM_FLDS              4
> @@ -1150,6 +1161,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..6643c00e11e 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -3930,6 +3930,24 @@ 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, all registers, except for stack register,
> +link register (r30), and frame pointer (r29), are shifted to be caller saved,
> +and can be used as scratch registers by the callee.
> +

The LLVM documentation says they are callee saved though, not caller.

So one of these seem odd.

> +Additionally, registers r20--r28, r0--r7, r10--r14, and finally r9 are used 
> for
> +argument passing, in that order.  The return value registers remain r0 and 
> r1.
> +

r15 is also an argument passing on non-Windows isn't it?

Thanks,
Tamar

> +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..a411af23256
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_1.c
> @@ -0,0 +1,142 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-schedule-insns2" } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +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..7a47190687d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_3.c
> @@ -0,0 +1,109 @@
> +/* { 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) {
> +  /* 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;
> +}
> +
> +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) {
> +  /* 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 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);
> +
> +  if (res != 22 * 23 / 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);
> +
> +  if (res != 23 * 24 / 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..22338c96711
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_4.c
> @@ -0,0 +1,93 @@
> +/* { dg-do compile } */
> +/* { 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;
> +}
> 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..87b22646fb1
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_5.c
> @@ -0,0 +1,45 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-schedule-insns2" } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +#include <stdarg.h>
> +int foo [[gnu::preserve_none]] (...);
> +
> +/* Check the pcs argument order is correct. Should be x20-28, x0-7, x10-14,
> x9, and that the return arg is x0 */
> +
> +/*
> +** bar:
> +** ...
> +**   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)
> +      + 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..e576df40e77
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_6.c
> @@ -0,0 +1,66 @@
> +/* { 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 (22, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
> 12,
> 13,
> +                                14, 15, 16, 17, 18, 19, 20, 21);
> +  if (res != 22 * 21 / 2)
> +    return 1;
> +
> +  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;
> +
> +  return 0;
> +}
> --
> 2.34.1

Reply via email to