Hi Alfie,

> On 19 Nov 2025, at 10:57, Alfie Richards <[email protected]> wrote:
> 
> The 11/18/2025 22:48, Iain Sandoe wrote:
>> Hi Alfie,
> 
> Hi Iain,
> 
>> 
>>> On 6 Nov 2025, at 15:06, Alfie Richards <[email protected]> wrote:
>> 
>>> Updated this patch to use r15 for argument passing (except on windows 
>>> targets)
>>> which I missed.
>> 
>> This causes this testcase:
>> https://github.com/iains/gcc-darwin-arm64/blob/master-wip-apple-si/gcc/testsuite/gcc.target/aarch64/darwin/darwinpcs-d1.c
>> 
>> to ICE on both aarch64-linux (cfarm185) and with my Darwin patches applied 
>> on arm64 macOS.
>> 
>> during RTL pass: expand
>> ../darwinpcs-d1.c: In function ‘call_use_no_stack’:
>> ../darwinpcs-d1.c:53:3: internal compiler error: in get_pcs_arg_reg, at 
>> config/aarch64/aarch64.cc:7362
>>   53 |   use_no_stack (0, 1, 2, 3, 4, 5, 6, 7, e, 'j', e, 'l');
>>      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> 0x231f353 internal_error(char const*, ...)
>> ../../src/gcc/diagnostic-global-context.cc:787
>> 0x84676f fancy_abort(char const*, int, char const*)
>> ../../src/gcc/diagnostics/context.cc:1805
>> 0x150e933 get_pcs_arg_reg
>> ../../src/gcc/config/aarch64/aarch64.cc:7362
>> 0x150e933 get_pcs_arg_reg
>> ../../src/gcc/config/aarch64/aarch64.cc:7358
>> 
>> I think from comparing the conditions used in the main loop with the assert 
>> here
>> the fix is:
>> 
>> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
>> index 72a3961fa2f..d643a25e752 100644
>> --- a/gcc/config/aarch64/aarch64.cc
>> +++ b/gcc/config/aarch64/aarch64.cc
>> @@ -7388,7 +7388,7 @@ 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));
>> +  gcc_assert (num <= num_pcs_arg_regs (pcs));
>>     switch (pcs)
>>     {
>> 
>> Does that seem reasonable, or did I miss some subtlety?
> 
> That doesnt seem correct to me. For AAPCS R0-7 are used for argument passing
> so num_pcs_arg_regs (pcs) is 8 so num < 8 seems to be the correct condition.
> 
> The checks in aarch64_layout_arg are to check that
> ncrn [next register to allocate] + nregs [number of registers required] <= 
> num_pcs_arg_regs (pcs) [number of argument passing registers available]
> so the highest argument register requested used should be ncrn + nregs - 1 so 
> the strict less than should
> still be valid.
> 
> I will look more into the darwin PCS and how it differs from AAPCS but it 
> seems like its
> an error in aarch64_layout_arg.

The darwinpcs differences are described here: 
https://github.com/iains/gcc-darwin-arm64/blob/master-wip-apple-si/gcc/config/aarch64/darwinpcs.md
  (which is markdown _not_ a machine description:) )

However, the test ICEs on aarch64-linux, and AFAICT  the source code should be 
valid there too - so the glitch is not Darwin-specific.

Iain

> 
> Thanks,
> Alfie
> 
>> 
>> thanks
>> Iain
>> 
>>> 
>>> 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?
>>> 
>>> 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
>>> 
>> 
> 
> -- 
> Alfie Richards


Reply via email to