*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;
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[] =
{
/* { 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,
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. */
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 ();
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;
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);
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. */
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
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",
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,
/* 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);
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));
}
}
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;
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;
}
/* 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. */
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 ())
{
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)
}
}
-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
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);
/* 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,
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)
{
{
/* 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. */
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"))
#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
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
};
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:
--- /dev/null
+/* { 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();
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}