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 >
