https://gcc.gnu.org/g:7197d8062fddc26c05c0da0bbfcd4128de788e34
commit r16-5076-g7197d8062fddc26c05c0da0bbfcd4128de788e34 Author: Alfie Richards <[email protected]> Date: Wed Oct 15 13:34:55 2025 +0000 aarch64: Add support for preserve_none function attribute [PR target/118328] 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. Diff: --- gcc/config/aarch64/aarch64.cc | 179 ++++++++++++++++++--- gcc/config/aarch64/aarch64.h | 27 ++++ gcc/doc/extend.texi | 21 +++ gcc/testsuite/gcc.target/aarch64/preserve_none_1.c | 143 ++++++++++++++++ gcc/testsuite/gcc.target/aarch64/preserve_none_2.c | 49 ++++++ gcc/testsuite/gcc.target/aarch64/preserve_none_3.c | 114 +++++++++++++ gcc/testsuite/gcc.target/aarch64/preserve_none_4.c | 99 ++++++++++++ gcc/testsuite/gcc.target/aarch64/preserve_none_5.c | 47 ++++++ gcc/testsuite/gcc.target/aarch64/preserve_none_6.c | 76 +++++++++ .../gcc.target/aarch64/preserve_none_mingw_1.c | 93 +++++++++++ 10 files changed, 828 insertions(+), 20 deletions(-) diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 74e2f20de4e1..b3e752bbd8cd 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 @@ -21777,8 +21898,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); @@ -22163,7 +22285,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, @@ -22187,8 +22310,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) { @@ -25494,7 +25631,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. */ @@ -30225,6 +30362,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 2cd929d83f96..0e596b597449 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 a5b97f37d9d3..616e05ebbd2c 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3970,6 +3970,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 000000000000..1390173836d7 --- /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 000000000000..1bb89e026e5a --- /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 000000000000..0258177f4228 --- /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 000000000000..fc8347d6c28f --- /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 000000000000..d11bf10a8985 --- /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 000000000000..687e30a063ef --- /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 000000000000..11703c8bb96e --- /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; +}
