From: Alfie Richards <[email protected]>

Hi all,

This patch is largely based on the draft version written by Richard S attached
to the bugzilla.

We would like to make the PCS ABI for this sync with LLVM, which I think it
does currently but it is not yet specified properly so may change later.

Reg tested on AArch64.

Ok for master?

Alfie

-- >8 --

When applied to a function preserve_none changes the procedure call standard
such that all registers except stack pointer, frame register, and link register
are caller saved. Additionally, changes the argument passing registers.

Additionally, changes the argument passing registers.

        PR target/118328

gcc/ChangeLog:

        * config/aarch64/aarch64.cc (handle_aarch64_vector_pcs_attribute):
        Add handling for ARM_PCS_PRESERVE_NONE.
        (aarch64_pcs_exclusions): New definition.
        (aarch64_gnu_attributes): Add entry for preserve_none and add
        aarch64_pcs_exclusions to aarch64_vector_pcs entry.
        (aarch64_preserve_none_abi): New function.
        (aarch64_fntype_abi): Add handling for preserve_none.
        (aarch64_reg_save_mode): Add handling for ARM_PCS_PRESERVE_NONE.
        (aarch64_hard_regno_call_part_clobbered): Add handling for
        ARM_PCS_PRESERVE_NONE.
        (num_pcs_arg_regs): New helper function.
        (get_pcs_arg_reg): New helper function.
        (aarch64_function_ok_for_sibcall): Add handling for 
ARM_PCS_PRESERVE_NONE.
        (aarch64_layout_arg): Add preserve_none argument lauout..
        (function_arg_preserve_none_regno_p): New helper function.
        (aarch64_function_arg): Update to handle preserve_none.
        (function_arg_preserve_none_regno_p): Update logic for preserve_none.
        (aarch64_expand_builtin_va_start): Add preserve_none layout.
        (aarch64_setup_incoming_varargs): Add preserve_none layout.
        (aarch64_is_variant_pcs): Update for case of ARM_PCS_PRESERVE_NONE.
        (aarch64_comp_type_attributes): Add preserve_none.
        * config/aarch64/aarch64.h (NUM_PRESERVE_NONE_ARG_REGS): New macro.
        (PRESERVE_NONE_REGISTERS): New macro.
        (enum arm_pcs): Add ARM_PCS_PRESERVE_NONE.
        * doc/extend.texi (preserve_none): Add docs for new attribute.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/preserve_none_1.c: New test.
        * gcc.target/aarch64/preserve_none_2.c: New test.
        * gcc.target/aarch64/preserve_none_3.c: New test.
        * gcc.target/aarch64/preserve_none_4.c: New test.
        * gcc.target/aarch64/preserve_none_5.c: New test.
        * gcc.target/aarch64/preserve_none_6.c: New test.

Co-authored-by: Richard Sandiford <[email protected]>

---
 gcc/config/aarch64/aarch64.cc                 | 178 ++++++++++++++++--
 gcc/config/aarch64/aarch64.h                  |  13 ++
 gcc/doc/extend.texi                           |  18 ++
 .../gcc.target/aarch64/preserve_none_1.c      | 142 ++++++++++++++
 .../gcc.target/aarch64/preserve_none_2.c      |  49 +++++
 .../gcc.target/aarch64/preserve_none_3.c      | 109 +++++++++++
 .../gcc.target/aarch64/preserve_none_4.c      |  93 +++++++++
 .../gcc.target/aarch64/preserve_none_5.c      |  45 +++++
 .../gcc.target/aarch64/preserve_none_6.c      |  66 +++++++
 9 files changed, 693 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/preserve_none_6.c

diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index f28d670b55f..9a94b4181b3 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -749,6 +749,8 @@ handle_aarch64_vector_pcs_attribute (tree *node, tree name, 
tree,
       *no_add_attrs = true;
       return NULL_TREE;
 
+      /* Rely on the exclusions list for preserve_none.  */
+    case ARM_PCS_PRESERVE_NONE:
     case ARM_PCS_TLSDESC:
     case ARM_PCS_UNKNOWN:
       break;
@@ -851,6 +853,16 @@ handle_arm_shared (tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
+/* Mutually-exclusive function type attributes for various PCS variants.  */
+static const struct attribute_spec::exclusions aarch64_pcs_exclusions[] =
+{
+  /* Attribute name     exclusion applies to:
+                       function, type, variable */
+  { "aarch64_vector_pcs", false, true, false },
+  { "preserve_none", false, true, false },
+  { NULL, false, false, false }
+};
+
 /* Mutually-exclusive function type attributes for controlling PSTATE.SM.  */
 static const struct attribute_spec::exclusions attr_streaming_exclusions[] =
 {
@@ -867,7 +879,10 @@ static const attribute_spec aarch64_gnu_attributes[] =
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "aarch64_vector_pcs", 0, 0, false, true,  true,  true,
-                         handle_aarch64_vector_pcs_attribute, NULL },
+                         handle_aarch64_vector_pcs_attribute,
+                         aarch64_pcs_exclusions },
+  { "preserve_none",      0, 0, false, true,  true,  true,  NULL,
+                         aarch64_pcs_exclusions },
   { "indirect_return",    0, 0, false, true, true, true, NULL, NULL },
   { "arm_sve_vector_bits", 1, 1, false, true,  false, true,
                          aarch64_sve::handle_arm_sve_vector_bits_attribute,
@@ -1317,6 +1332,23 @@ aarch64_sve_abi (void)
   return sve_abi;
 }
 
+/* Return the descriptor of the preserve_none PCS.  */
+
+static const predefined_function_abi &
+aarch64_preserve_none_abi (void)
+{
+  auto &preserve_none_abi = function_abis[ARM_PCS_PRESERVE_NONE];
+  if (!preserve_none_abi.initialized_p ())
+    {
+      HARD_REG_SET preserved_regs = {};
+      if (!CALL_USED_X18)
+       SET_HARD_REG_BIT (preserved_regs, R18_REGNUM);
+      auto full_reg_clobbers = reg_class_contents[ALL_REGS] & ~preserved_regs;
+      preserve_none_abi.initialize (ARM_PCS_PRESERVE_NONE, full_reg_clobbers);
+    }
+  return preserve_none_abi;
+}
+
 /* If X is an UNSPEC_SALT_ADDR expression, return the address that it
    wraps, otherwise return X itself.  */
 
@@ -2312,6 +2344,9 @@ aarch64_fntype_abi (const_tree fntype)
   if (lookup_attribute ("aarch64_vector_pcs", TYPE_ATTRIBUTES (fntype)))
     return aarch64_simd_abi ();
 
+  if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (fntype)))
+    return aarch64_preserve_none_abi ();
+
   if (aarch64_returns_value_in_sve_regs_p (fntype)
       || aarch64_takes_arguments_in_sve_regs_p (fntype))
     return aarch64_sve_abi ();
@@ -2519,6 +2554,9 @@ aarch64_reg_save_mode (unsigned int regno)
   if (FP_REGNUM_P (regno))
     switch (crtl->abi->id ())
       {
+      case ARM_PCS_PRESERVE_NONE:
+       /* We should never save FPRs for preserve_none, but nevertheless
+          treat it like the base PCS for consistency.  */
       case ARM_PCS_AAPCS64:
        /* Only the low 64 bits are saved by the base PCS.  */
        return DFmode;
@@ -2649,7 +2687,9 @@ aarch64_hard_regno_call_part_clobbered (unsigned int 
abi_id,
                                        unsigned int regno,
                                        machine_mode mode)
 {
-  if (FP_REGNUM_P (regno) && abi_id != ARM_PCS_SVE)
+  if (FP_REGNUM_P (regno)
+      && abi_id != ARM_PCS_SVE
+      && abi_id != ARM_PCS_PRESERVE_NONE)
     {
       poly_int64 per_register_size = GET_MODE_SIZE (mode);
       unsigned int nregs = hard_regno_nregs (regno, mode);
@@ -6826,6 +6866,10 @@ aarch64_function_ok_for_sibcall (tree, tree exp)
   auto from_abi = crtl->abi->id ();
   auto to_abi = expr_callee_abi (exp).id ();
 
+  /* preserve_none functions can tail-call anything that the base PCS can.  */
+  if (from_abi != to_abi && from_abi == ARM_PCS_PRESERVE_NONE)
+    from_abi = ARM_PCS_AAPCS64;
+
   /* ARM_PCS_SVE preserves strictly more than ARM_PCS_SIMD, which in
      turn preserves strictly more than the base PCS.  The callee must
      preserve everything that the caller is required to preserve.  */
@@ -7287,6 +7331,49 @@ bitint_or_aggr_of_bitint_p (tree type)
   return false;
 }
 
+/* How many GPR are available for argument passing in the procedure call
+   standard.  */
+static int
+num_pcs_arg_regs (enum arm_pcs pcs)
+{
+  switch (pcs)
+    {
+    case ARM_PCS_PRESERVE_NONE:
+      return NUM_PRESERVE_NONE_ARG_REGS;
+    case ARM_PCS_AAPCS64:
+    case ARM_PCS_SIMD:
+    case ARM_PCS_SVE:
+    case ARM_PCS_TLSDESC:
+    case ARM_PCS_UNKNOWN:
+      return NUM_ARG_REGS;
+    }
+  gcc_unreachable ();
+}
+
+/* Get the NUM'th GPR argument passing register from the PCS procedure call
+ * standard.  */
+
+static int
+get_pcs_arg_reg (enum arm_pcs pcs, int num)
+{
+  static const int ARM_PCS_PRESERVE_NONE_REGISTERS[] = PRESERVE_NONE_REGISTERS;
+
+  gcc_assert (num < num_pcs_arg_regs (pcs));
+
+  switch (pcs)
+    {
+    case ARM_PCS_PRESERVE_NONE:
+      return ARM_PCS_PRESERVE_NONE_REGISTERS[num];
+    case ARM_PCS_AAPCS64:
+    case ARM_PCS_SIMD:
+    case ARM_PCS_SVE:
+    case ARM_PCS_TLSDESC:
+    case ARM_PCS_UNKNOWN:
+      return R0_REGNUM + num;
+    }
+  gcc_unreachable ();
+}
+
 /* Layout a function argument according to the AAPCS64 rules.  The rule
    numbers refer to the rule numbers in the AAPCS64.  ORIG_MODE is the
    mode that was originally given to us by the target hook, whereas the
@@ -7385,7 +7472,9 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
         unprototyped function.  There is no ABI-defined location we
         can return in this case, so we have no real choice but to raise
         an error immediately, even though this is only a query function.  */
-      if (arg.named && pcum->pcs_variant != ARM_PCS_SVE)
+      if (arg.named
+         && pcum->pcs_variant != ARM_PCS_SVE
+         && pcum->pcs_variant != ARM_PCS_PRESERVE_NONE)
        {
          gcc_assert (!pcum->silent_p);
          error ("SVE type %qT cannot be passed to an unprototyped function",
@@ -7400,7 +7489,6 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
       pcum->aapcs_nextnvrn = pcum->aapcs_nvrn + pst_info.num_zr ();
       pcum->aapcs_nextnprn = pcum->aapcs_nprn + pst_info.num_pr ();
       gcc_assert (arg.named
-                 && pcum->pcs_variant == ARM_PCS_SVE
                  && pcum->aapcs_nextnvrn <= NUM_FP_ARG_REGS
                  && pcum->aapcs_nextnprn <= NUM_PR_ARG_REGS);
       pcum->aapcs_reg = pst_info.get_rtx (mode, V0_REGNUM + pcum->aapcs_nvrn,
@@ -7514,7 +7602,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
   /* C6 - C9.  though the sign and zero extension semantics are
      handled elsewhere.  This is the case where the argument fits
      entirely general registers.  */
-  if (allocate_ncrn && (ncrn + nregs <= NUM_ARG_REGS))
+  if (allocate_ncrn && (ncrn + nregs <= num_pcs_arg_regs (pcum->pcs_variant)))
     {
       gcc_assert (nregs == 0 || nregs == 1 || nregs == 2);
 
@@ -7550,7 +7638,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
                inform (input_location, "parameter passing for argument of type 
"
                        "%qT changed in GCC 9.1", type);
              ++ncrn;
-             gcc_assert (ncrn + nregs <= NUM_ARG_REGS);
+             gcc_assert (ncrn + nregs <= num_pcs_arg_regs (pcum->pcs_variant));
            }
        }
 
@@ -7572,7 +7660,8 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
       if (nregs == 0
          || (nregs == 1 && !sve_p)
          || GET_MODE_CLASS (mode) == MODE_INT)
-       pcum->aapcs_reg = gen_rtx_REG (mode, R0_REGNUM + ncrn);
+       pcum->aapcs_reg
+         = gen_rtx_REG (mode, get_pcs_arg_reg (pcum->pcs_variant, ncrn));
       else
        {
          rtx par;
@@ -7584,7 +7673,8 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
              scalar_int_mode reg_mode = word_mode;
              if (nregs == 1)
                reg_mode = int_mode_for_mode (mode).require ();
-             rtx tmp = gen_rtx_REG (reg_mode, R0_REGNUM + ncrn + i);
+             int reg = get_pcs_arg_reg (pcum->pcs_variant, ncrn + i);
+             rtx tmp = gen_rtx_REG (reg_mode, reg);
              tmp = gen_rtx_EXPR_LIST (VOIDmode, tmp,
                                       GEN_INT (i * UNITS_PER_WORD));
              XVECEXP (par, 0, i) = tmp;
@@ -7597,7 +7687,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
     }
 
   /* C.11  */
-  pcum->aapcs_nextncrn = NUM_ARG_REGS;
+  pcum->aapcs_nextncrn = num_pcs_arg_regs (pcum->pcs_variant);
 
   /* The argument is passed on stack; record the needed number of words for
      this argument and align the total size if necessary.  */
@@ -7675,7 +7765,8 @@ aarch64_function_arg (cumulative_args_t pcum_v, const 
function_arg_info &arg)
   CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
   gcc_assert (pcum->pcs_variant == ARM_PCS_AAPCS64
              || pcum->pcs_variant == ARM_PCS_SIMD
-             || pcum->pcs_variant == ARM_PCS_SVE);
+             || pcum->pcs_variant == ARM_PCS_SVE
+             || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE);
 
   if (arg.end_marker_p ())
     {
@@ -7767,7 +7858,8 @@ aarch64_function_arg_advance (cumulative_args_t pcum_v,
   CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
   if (pcum->pcs_variant == ARM_PCS_AAPCS64
       || pcum->pcs_variant == ARM_PCS_SIMD
-      || pcum->pcs_variant == ARM_PCS_SVE)
+      || pcum->pcs_variant == ARM_PCS_SVE
+      || pcum->pcs_variant == ARM_PCS_PRESERVE_NONE)
     {
       aarch64_layout_arg (pcum_v, arg);
       gcc_assert ((pcum->aapcs_reg != NULL_RTX)
@@ -7786,13 +7878,41 @@ aarch64_function_arg_advance (cumulative_args_t pcum_v,
     }
 }
 
-bool
-aarch64_function_arg_regno_p (unsigned regno)
+/* Checks if a register is live at entry of a preserve_none pcs function.
+   That is, it used for passing registers.  See ARM_PCS_PRESERVE_NONE_REGISTERS
+   for full list and order of argument passing registers.  */
+
+static bool
+function_arg_preserve_none_regno_p (unsigned regno)
 {
-  return ((GP_REGNUM_P (regno) && regno < R0_REGNUM + NUM_ARG_REGS)
+  return ((GP_REGNUM_P (regno) && regno != R8_REGNUM && regno != R15_REGNUM
+          && regno != R16_REGNUM && regno != R17_REGNUM && regno != R18_REGNUM
+          && regno != R19_REGNUM && regno != R29_REGNUM && regno != R30_REGNUM)
          || (FP_REGNUM_P (regno) && regno < V0_REGNUM + NUM_FP_ARG_REGS)
          || (PR_REGNUM_P (regno) && regno < P0_REGNUM + NUM_PR_ARG_REGS));
 }
+/* Implements FUNCTION_ARG_REGNO_P.  */
+bool
+aarch64_function_arg_regno_p (unsigned regno)
+{
+  enum arm_pcs pcs
+    = cfun ? (arm_pcs) fndecl_abi (cfun->decl).id () : ARM_PCS_AAPCS64;
+
+  switch (pcs)
+    {
+    case ARM_PCS_AAPCS64:
+    case ARM_PCS_SIMD:
+    case ARM_PCS_SVE:
+    case ARM_PCS_TLSDESC:
+    case ARM_PCS_UNKNOWN:
+      return ((GP_REGNUM_P (regno) && regno < R0_REGNUM + NUM_ARG_REGS)
+             || (FP_REGNUM_P (regno) && regno < V0_REGNUM + NUM_FP_ARG_REGS)
+             || (PR_REGNUM_P (regno) && regno < P0_REGNUM + NUM_PR_ARG_REGS));
+    case ARM_PCS_PRESERVE_NONE:
+      return function_arg_preserve_none_regno_p (regno);
+    }
+  gcc_unreachable ();
+}
 
 /* Implement FUNCTION_ARG_BOUNDARY.  Every parameter gets at least
    PARM_BOUNDARY bits of alignment, but will be given anything up
@@ -21804,8 +21924,9 @@ aarch64_expand_builtin_va_start (tree valist, rtx 
nextarg ATTRIBUTE_UNUSED)
 
   cum = &crtl->args.info;
   if (cfun->va_list_gpr_size)
-    gr_save_area_size = MIN ((NUM_ARG_REGS - cum->aapcs_ncrn) * UNITS_PER_WORD,
-                            cfun->va_list_gpr_size);
+    gr_save_area_size = MIN ((num_pcs_arg_regs (cum->pcs_variant)
+                             - cum->aapcs_ncrn)
+                            * UNITS_PER_WORD, cfun->va_list_gpr_size);
   if (cfun->va_list_fpr_size)
     vr_save_area_size = MIN ((NUM_FP_ARG_REGS - cum->aapcs_nvrn)
                             * UNITS_PER_VREG, cfun->va_list_fpr_size);
@@ -22190,7 +22311,8 @@ aarch64_setup_incoming_varargs (cumulative_args_t cum_v,
   /* Found out how many registers we need to save.
      Honor tree-stdvar analysis results.  */
   if (cfun->va_list_gpr_size)
-    gr_saved = MIN (NUM_ARG_REGS - local_cum.aapcs_ncrn,
+    gr_saved = MIN (num_pcs_arg_regs (local_cum.pcs_variant)
+                   - local_cum.aapcs_ncrn,
                    cfun->va_list_gpr_size / UNITS_PER_WORD);
   if (cfun->va_list_fpr_size)
     vr_saved = MIN (NUM_FP_ARG_REGS - local_cum.aapcs_nvrn,
@@ -22214,8 +22336,22 @@ aarch64_setup_incoming_varargs (cumulative_args_t 
cum_v,
          mem = gen_frame_mem (BLKmode, ptr);
          set_mem_alias_set (mem, get_varargs_alias_set ());
 
-         move_block_from_reg (local_cum.aapcs_ncrn + R0_REGNUM,
-                              mem, gr_saved);
+         /* For preserve_none pcs we can't use move_block_from_reg as the
+            argument passing register order is not consecutive.  */
+         if (local_cum.pcs_variant == ARM_PCS_PRESERVE_NONE)
+           {
+             for (int i = 0; i < gr_saved; ++i)
+               {
+                 rtx tem = operand_subword (mem, i, 1, BLKmode);
+                 gcc_assert (tem);
+                 int reg = get_pcs_arg_reg (local_cum.pcs_variant,
+                                            local_cum.aapcs_ncrn + i);
+                 emit_move_insn (tem, gen_rtx_REG (word_mode, reg));
+               }
+           }
+         else
+           move_block_from_reg (R0_REGNUM + local_cum.aapcs_ncrn, mem,
+                                gr_saved);
        }
       if (vr_saved > 0)
        {
@@ -25521,7 +25657,7 @@ aarch64_is_variant_pcs (tree fndecl)
 {
   /* Check for ABIs that preserve more registers than usual.  */
   arm_pcs pcs = (arm_pcs) fndecl_abi (fndecl).id ();
-  if (pcs == ARM_PCS_SIMD || pcs == ARM_PCS_SVE)
+  if (pcs == ARM_PCS_SIMD || pcs == ARM_PCS_SVE || pcs == 
ARM_PCS_PRESERVE_NONE)
     return true;
 
   /* Check for ABIs that allow PSTATE.SM to be 1 on entry.  */
@@ -30252,6 +30388,8 @@ aarch64_comp_type_attributes (const_tree type1, 
const_tree type2)
 
   if (!check_attr ("gnu", "aarch64_vector_pcs"))
     return 0;
+  if (!check_attr ("gnu", "preserve_none"))
+    return 0;
   if (!check_attr ("gnu", "indirect_return"))
     return 0;
   if (!check_attr ("gnu", "Advanced SIMD type"))
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 2cd929d83f9..79528696da0 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -696,6 +696,17 @@ through +ssve-fp8dot2.  */
 #define NUM_FP_ARG_REGS                        8
 #define NUM_PR_ARG_REGS                        4
 
+/* The argument passing regs for preserve_none pcs.  */
+#define NUM_PRESERVE_NONE_ARG_REGS 23
+#define PRESERVE_NONE_REGISTERS \
+{ \
+  R20_REGNUM, R21_REGNUM, R22_REGNUM, R23_REGNUM, R24_REGNUM, R25_REGNUM,\
+  R26_REGNUM, R27_REGNUM, R28_REGNUM,\
+  R0_REGNUM, R1_REGNUM, R2_REGNUM, R3_REGNUM, R4_REGNUM, R5_REGNUM,\
+  R6_REGNUM, R7_REGNUM,\
+  R10_REGNUM, R11_REGNUM, R12_REGNUM, R13_REGNUM, R14_REGNUM, R9_REGNUM\
+}
+
 /* A Homogeneous Floating-Point or Short-Vector Aggregate may have at most
    four members.  */
 #define HA_MAX_NUM_FLDS                4
@@ -1150,6 +1161,8 @@ enum arm_pcs
   ARM_PCS_SVE,                 /* For functions that pass or return
                                   values in SVE registers.  */
   ARM_PCS_TLSDESC,             /* For targets of tlsdesc calls.  */
+  ARM_PCS_PRESERVE_NONE,       /* PCS variant with no call-preserved
+                                  registers except X29.  */
   ARM_PCS_UNKNOWN
 };
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 94b76b75565..231551d6a16 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3930,6 +3930,24 @@ threads, such as the POSIX @code{swapcontext} function.  
This attribute
 adds a @code{BTI J} instruction when BTI is enabled e.g. via
 @option{-mbranch-protection}.
 
+@cindex @code{preserve_none} function attribute, AArch64
+@item preserve_none
+Use this attribute to change the procedure call standard of the specified
+function to the preserve_none variant.
+
+The preserve-none abi variant modifies the AAPCS such that has no
+callee-saved registers (including SIMD and floating point registers).
+That is, all registers, except for stack register,
+link register (r30) and frame pointer (r29), are shifted to be caller saved,
+and can be used as scratch registers by the callee.
+
+Additionally, registers r20-r28, r0-r7, r10-r14, and finally r9 are used for
+argument passing, in that order.  The return value registers remain r0 and r1.
+
+All other details are the same as for the AAPCS ABI.
+
+This ABI has not been stabilized, and may be subject to change in future
+versions.
 @end table
 
 The above target attributes can be specified as follows:
diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_1.c 
b/gcc/testsuite/gcc.target/aarch64/preserve_none_1.c
new file mode 100644
index 00000000000..a411af23256
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_1.c
@@ -0,0 +1,142 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-schedule-insns2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+void normal_callee();
+void preserve_none_callee() [[gnu::preserve_none]];
+
+#pragma GCC target "+sve"
+
+/*
+** preserve_none_caller1:
+** ?#APP
+**     nop
+** ?#NO_APP
+**     ret
+*/
+void preserve_none_caller1() [[gnu::preserve_none]]
+{
+  asm volatile ("nop" ::: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+               "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+               "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+               "x24", "x25", "x26", "x27", "x28",
+
+               "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7",
+               "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15",
+               "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23",
+               "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31",
+
+               "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7",
+               "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15");
+}
+
+/*
+** preserve_none_caller2:
+**     stp     x29, x30, \[sp, #?-16\]!
+**     mov     x29, sp
+**     bl      normal_callee
+**     mov     w0, w20
+**     ldp     x29, x30, \[sp\], #?16
+**     ret
+*/
+int preserve_none_caller2(int x) [[gnu::preserve_none]]
+{
+  normal_callee();
+  return x;
+}
+
+/*
+** preserve_none_caller3:
+**     stp     x29, x30, \[sp, #?-32\]!
+**     mov     x29, sp
+**     str     w20, \[sp, #?[0-9]+\]
+**     bl      preserve_none_callee
+**     ldr     w0, \[sp, #?[0-9]+\]
+**     ldp     x29, x30, \[sp\], #?32
+**     ret
+*/
+int preserve_none_caller3(int x) [[gnu::preserve_none]]
+{
+  preserve_none_callee();
+  return x;
+}
+
+/*
+** preserve_none_caller4:
+**     b       preserve_none_callee
+*/
+void preserve_none_caller4() [[gnu::preserve_none]]
+{
+  preserve_none_callee();
+}
+
+/*
+** preserve_none_caller5:
+**     b       preserve_none_callee
+*/
+void preserve_none_caller5(__SVBool_t x) [[gnu::preserve_none]]
+{
+  preserve_none_callee();
+}
+
+/*
+** normal_caller1:
+**     stp     x29, x30, \[sp, #?-160\]!
+**     mov     x29, sp
+**     stp     x19, x20, \[sp, #?16\]
+**     stp     x21, x22, \[sp, #?32\]
+**     stp     x23, x24, \[sp, #?48\]
+**     stp     x25, x26, \[sp, #?64\]
+**     stp     x27, x28, \[sp, #?80\]
+**     stp     d8, d9, \[sp, #?96\]
+**     stp     d10, d11, \[sp, #?112\]
+**     stp     d12, d13, \[sp, #?128\]
+**     stp     d14, d15, \[sp, #?144\]
+**     bl      preserve_none_callee
+**     ldp     d8, d9, \[sp, #?96\]
+**     ldp     d10, d11, \[sp, #?112\]
+**     ldp     d12, d13, \[sp, #?128\]
+**     ldp     d14, d15, \[sp, #?144\]
+**     ldp     x19, x20, \[sp, #?16\]
+**     ldp     x21, x22, \[sp, #?32\]
+**     ldp     x23, x24, \[sp, #?48\]
+**     ldp     x25, x26, \[sp, #?64\]
+**     ldp     x27, x28, \[sp, #?80\]
+**     ldp     x29, x30, \[sp\], #?160
+**     ret
+*/
+void normal_caller1()
+{
+  preserve_none_callee();
+}
+
+/*
+** normal_caller2:
+**     stp     x29, x30, \[sp, #?-160\]!
+**     mov     x29, sp
+**     stp     x19, x20, \[sp, #?16\]
+**     stp     x21, x22, \[sp, #?32\]
+**     stp     x23, x24, \[sp, #?48\]
+**     stp     x25, x26, \[sp, #?64\]
+**     stp     x27, x28, \[sp, #?80\]
+**     stp     d8, d9, \[sp, #?96\]
+**     stp     d10, d11, \[sp, #?112\]
+**     stp     d12, d13, \[sp, #?128\]
+**     stp     d14, d15, \[sp, #?144\]
+**     blr     x0
+**     ldp     d8, d9, \[sp, #?96\]
+**     ldp     d10, d11, \[sp, #?112\]
+**     ldp     d12, d13, \[sp, #?128\]
+**     ldp     d14, d15, \[sp, #?144\]
+**     ldp     x19, x20, \[sp, #?16\]
+**     ldp     x21, x22, \[sp, #?32\]
+**     ldp     x23, x24, \[sp, #?48\]
+**     ldp     x25, x26, \[sp, #?64\]
+**     ldp     x27, x28, \[sp, #?80\]
+**     ldp     x29, x30, \[sp\], #?160
+**     ret
+*/
+void normal_caller2(void (*callee)() [[gnu::preserve_none]])
+{
+  callee();
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_2.c 
b/gcc/testsuite/gcc.target/aarch64/preserve_none_2.c
new file mode 100644
index 00000000000..1bb89e026e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_2.c
@@ -0,0 +1,49 @@
+/* { dg-options "" } */
+
+void multi1() [[gnu::aarch64_vector_pcs, gnu::preserve_none]]; /* { dg-warning 
{ignoring attribute 'preserve_none' because it conflicts} } */
+void multi2() [[gnu::preserve_none, gnu::aarch64_vector_pcs]]; /* { dg-warning 
{ignoring attribute 'aarch64_vector_pcs' because it conflicts} } */
+
+void normal_callee();
+void preserve_none_callee() [[gnu::preserve_none]];
+void vector_callee() [[gnu::aarch64_vector_pcs]];
+void sve_callee(__SVBool_t);
+void sve_preserve_none_callee(__SVBool_t) [[gnu::preserve_none]];
+
+void (*normal_ptr)();
+void (*preserve_none_ptr)() [[gnu::preserve_none]];
+void (*vector_ptr)() [[gnu::aarch64_vector_pcs]];
+void (*sve_ptr)(__SVBool_t);
+void (*sve_preserve_none_ptr)(__SVBool_t) [[gnu::preserve_none]];
+
+void f()
+{
+  normal_ptr = normal_callee;
+  normal_ptr = preserve_none_callee; /* { dg-error {incompatible pointer type} 
} */
+  normal_ptr = vector_callee; /* { dg-error {incompatible pointer type} } */
+  normal_ptr = sve_callee; /* { dg-error {incompatible pointer type} } */
+  normal_ptr = sve_preserve_none_callee; /* { dg-error {incompatible pointer 
type} } */
+
+  preserve_none_ptr = normal_callee; /* { dg-error {incompatible pointer type} 
} */
+  preserve_none_ptr = preserve_none_callee;
+  preserve_none_ptr = vector_callee; /* { dg-error {incompatible pointer type} 
} */
+  preserve_none_ptr = sve_callee; /* { dg-error {incompatible pointer type} } 
*/
+  preserve_none_ptr = sve_preserve_none_callee; /* { dg-error {incompatible 
pointer type} } */
+
+  vector_ptr = normal_callee; /* { dg-error {incompatible pointer type} } */
+  vector_ptr = preserve_none_callee; /* { dg-error {incompatible pointer type} 
} */
+  vector_ptr = vector_callee;
+  vector_ptr = sve_callee; /* { dg-error {incompatible pointer type} } */
+  vector_ptr = sve_preserve_none_callee; /* { dg-error {incompatible pointer 
type} } */
+
+  sve_ptr = normal_callee; /* { dg-error {incompatible pointer type} } */
+  sve_ptr = preserve_none_callee; /* { dg-error {incompatible pointer type} } 
*/
+  sve_ptr = vector_callee; /* { dg-error {incompatible pointer type} } */
+  sve_ptr = sve_callee;
+  sve_ptr = sve_preserve_none_callee; /* { dg-error {incompatible pointer 
type} } */
+
+  sve_preserve_none_ptr = normal_callee; /* { dg-error {incompatible pointer 
type} } */
+  sve_preserve_none_ptr = preserve_none_callee; /* { dg-error {incompatible 
pointer type} } */
+  sve_preserve_none_ptr = vector_callee; /* { dg-error {incompatible pointer 
type} } */
+  sve_preserve_none_ptr = sve_callee; /* { dg-error {incompatible pointer 
type} } */
+  sve_preserve_none_ptr = sve_preserve_none_callee;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_3.c 
b/gcc/testsuite/gcc.target/aarch64/preserve_none_3.c
new file mode 100644
index 00000000000..7a47190687d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_3.c
@@ -0,0 +1,109 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -std=gnu23" } */
+
+int no_arg_stack_use_callee [[gnu::preserve_none, gnu::noinline, gnu::noipa]]
+                       (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
+                        int a7, int a8, int a9, int a10, int a11, int a12,
+                        int a13, int a14, int a15, int a16, int a17, int a18,
+                        int a19, int a20, int a21, int a22) {
+  /* Clobber all the registers to check they are correctly marked live at the
+     start.  */
+  asm volatile ("mov x0, #0;"
+               "mov x1, #0;"
+               "mov x2, #0;"
+               "mov x3, #0;"
+               "mov x4, #0;"
+               "mov x5, #0;"
+               "mov x6, #0;"
+               "mov x7, #0;"
+               "mov x8, #0;"
+               "mov x9, #0;"
+               "mov x10, #0;"
+               "mov x11, #0;"
+               "mov x12, #0;"
+               "mov x13, #0;"
+               "mov x14, #0;"
+               "mov x15, #0;"
+               "mov x16, #0;"
+               "mov x17, #0;"
+               "mov x18, #0;"
+               "mov x19, #0;"
+               "mov x20, #0;"
+               "mov x21, #0;"
+               "mov x22, #0;"
+               "mov x23, #0;"
+               "mov x24, #0;"
+               "mov x25, #0;"
+               "mov x26, #0;"
+               "mov x27, #0;"
+               "mov x28, #0;"
+               ::: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+               "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+               "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+               "x24", "x25", "x26", "x27", "x28");
+
+  return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + 
a13
+        + a14 + a15 + a16 + a17 + a18 + a19 + a20 + a21 + a22;
+}
+
+int arg_stack_use_callee [[gnu::preserve_none, gnu::noinline, gnu::noipa]]
+                       (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
+                        int a7, int a8, int a9, int a10, int a11, int a12,
+                        int a13, int a14, int a15, int a16, int a17, int a18,
+                        int a19, int a20, int a21, int a22, int a23) {
+  /* Clobber all the registers to check they are correctly marked live at the
+     start.  */
+  asm volatile ("mov x0, #0;"
+               "mov x1, #0;"
+               "mov x2, #0;"
+               "mov x3, #0;"
+               "mov x4, #0;"
+               "mov x5, #0;"
+               "mov x6, #0;"
+               "mov x7, #0;"
+               "mov x8, #0;"
+               "mov x9, #0;"
+               "mov x10, #0;"
+               "mov x11, #0;"
+               "mov x12, #0;"
+               "mov x13, #0;"
+               "mov x14, #0;"
+               "mov x15, #0;"
+               "mov x16, #0;"
+               "mov x17, #0;"
+               "mov x18, #0;"
+               "mov x19, #0;"
+               "mov x20, #0;"
+               "mov x21, #0;"
+               "mov x22, #0;"
+               "mov x23, #0;"
+               "mov x24, #0;"
+               "mov x25, #0;"
+               "mov x26, #0;"
+               "mov x27, #0;"
+               "mov x28, #0;"
+               ::: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+               "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+               "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+               "x24", "x25", "x26", "x27", "x28");
+
+  return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + 
a13
+        + a14 + a15 + a16 + a17 + a18 + a19 + a20 + a21 + a22 + a23;
+}
+
+int main () {
+
+  int res = no_arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
13, 14,
+                              15, 16, 17, 18, 19, 20, 21, 22);
+
+  if (res != 22 * 23 / 2)
+    return 1;
+
+  res = arg_stack_use_callee(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+                              15, 16, 17, 18, 19, 20, 21, 22, 23);
+
+  if (res != 23 * 24 / 2)
+    return 1;
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_4.c 
b/gcc/testsuite/gcc.target/aarch64/preserve_none_4.c
new file mode 100644
index 00000000000..22338c96711
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_4.c
@@ -0,0 +1,93 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-schedule-insns2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+int no_arg_stack_use_callee [[gnu::preserve_none, gnu::noinline, gnu::noipa]]
+                       (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
+                        int a7, int a8, int a9, int a10, int a11, int a12,
+                        int a13, int a14, int a15, int a16, int a17, int a18,
+                        int a19, int a20, int a21, int a22);
+
+/* Check the pcs argument order is correct. Should be x20-28, x0-7, x10-14, 
x9, and that the return arg is x0 */
+
+/*
+** no_arg_stack_use_caller:
+** ...
+**     mov     w9, 22
+**     mov     w14, 21
+**     mov     w13, 20
+**     mov     w12, 19
+**     mov     w11, 18
+**     mov     w10, 17
+**     mov     w7, 16
+**     mov     w6, 15
+**     mov     w5, 14
+**     mov     w4, 13
+**     mov     w3, 12
+**     mov     w2, 11
+**     mov     w1, 10
+**     mov     w0, 9
+**     mov     w28, 8
+**     mov     w27, 7
+**     mov     w26, 6
+**     mov     w25, 5
+**     mov     w24, 4
+**     mov     w23, 3
+**     mov     w22, 2
+**     mov     w21, 1
+**     mov     w20, 0
+**     bl      no_arg_stack_use_callee
+**     add     w0, w0, 1
+** ...
+*/
+int no_arg_stack_use_caller [[gnu::preserve_none]] ()
+{
+  return no_arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+                                 14, 15, 16, 17, 18, 19, 20, 21, 22)
+        + 1;
+}
+
+int arg_stack_use_callee [[gnu::preserve_none, gnu::noinline, gnu::noipa]]
+                       (int a0, int a1, int a2, int a3, int a4, int a5, int a6,
+                        int a7, int a8, int a9, int a10, int a11, int a12,
+                        int a13, int a14, int a15, int a16, int a17, int a18,
+                        int a19, int a20, int a21, int a22, int a23);
+
+/*
+** arg_stack_use_caller:
+** ...
+**     mov     w0, 23
+**     mov     w9, 22
+**     mov     w14, 21
+**     mov     w13, 20
+**     mov     w12, 19
+**     mov     w11, 18
+**     mov     w10, 17
+**     mov     w7, 16
+**     mov     w6, 15
+**     mov     w5, 14
+**     mov     w4, 13
+**     mov     w3, 12
+**     mov     w2, 11
+**     mov     w1, 10
+**     mov     w28, 8
+**     mov     w27, 7
+**     mov     w26, 6
+**     mov     w25, 5
+**     mov     w24, 4
+**     mov     w23, 3
+**     mov     w22, 2
+**     mov     w21, 1
+**     mov     w20, 0
+**     str     w0, \[sp\]
+**     mov     w0, 9
+**     bl      arg_stack_use_callee
+**     add     w0, w0, 1
+** ...
+*/
+int arg_stack_use_caller [[gnu::preserve_none]] ()
+{
+  return arg_stack_use_callee (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 
14,
+                              15, 16, 17, 18, 19, 20, 21, 22, 23)
+        + 1;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_5.c 
b/gcc/testsuite/gcc.target/aarch64/preserve_none_5.c
new file mode 100644
index 00000000000..87b22646fb1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_5.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-schedule-insns2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <stdarg.h>
+int foo [[gnu::preserve_none]] (...);
+
+/* Check the pcs argument order is correct. Should be x20-28, x0-7, x10-14, 
x9, and that the return arg is x0 */
+
+/*
+** bar:
+** ...
+**     mov     w9, 22
+**     mov     w14, 21
+**     mov     w13, 20
+**     mov     w12, 19
+**     mov     w11, 18
+**     mov     w10, 17
+**     mov     w7, 16
+**     mov     w6, 15
+**     mov     w5, 14
+**     mov     w4, 13
+**     mov     w3, 12
+**     mov     w2, 11
+**     mov     w1, 10
+**     mov     w0, 9
+**     mov     w28, 8
+**     mov     w27, 7
+**     mov     w26, 6
+**     mov     w25, 5
+**     mov     w24, 4
+**     mov     w23, 3
+**     mov     w22, 2
+**     mov     w21, 1
+**     mov     w20, 0
+**     bl      foo
+**     add     w0, w0, 1
+** ...
+*/
+int bar [[gnu::preserve_none]] ()
+{
+  return foo (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+             19, 20, 21, 22)
+        + 1;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/preserve_none_6.c 
b/gcc/testsuite/gcc.target/aarch64/preserve_none_6.c
new file mode 100644
index 00000000000..e576df40e77
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/preserve_none_6.c
@@ -0,0 +1,66 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -std=gnu23" } */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+int preserve_none_va_func [[gnu::preserve_none, gnu::noinline, gnu::noclone]] 
(int count, ...) {
+  asm volatile ("mov x0, #0;"
+               "mov x1, #0;"
+               "mov x2, #0;"
+               "mov x3, #0;"
+               "mov x4, #0;"
+               "mov x5, #0;"
+               "mov x6, #0;"
+               "mov x7, #0;"
+               "mov x8, #0;"
+               "mov x9, #0;"
+               "mov x10, #0;"
+               "mov x11, #0;"
+               "mov x12, #0;"
+               "mov x13, #0;"
+               "mov x14, #0;"
+               "mov x15, #0;"
+               "mov x16, #0;"
+               "mov x17, #0;"
+               "mov x18, #0;"
+               "mov x19, #0;"
+               "mov x20, #0;"
+               "mov x21, #0;"
+               "mov x22, #0;"
+               "mov x23, #0;"
+               "mov x24, #0;"
+               "mov x25, #0;"
+               "mov x26, #0;"
+               "mov x27, #0;"
+               "mov x28, #0;"
+               ::: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+               "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+               "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+               "x24", "x25", "x26", "x27", "x28");
+
+  int sum = 0;
+
+  va_list args;
+
+  va_start (args, count);
+  for (int i = 0; i < count; i++)
+    sum += va_arg(args, int);
+  va_end (args);
+
+  return sum;
+}
+
+int main () {
+  int res = preserve_none_va_func (22, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
12, 13,
+                                  14, 15, 16, 17, 18, 19, 20, 21);
+  if (res != 22 * 21 / 2)
+    return 1;
+
+  res = preserve_none_va_func (23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
13,
+                                  14, 15, 16, 17, 18, 19, 20, 21, 22);
+  if (res != 23 * 22 / 2)
+    return 1;
+
+  return 0;
+}
-- 
2.34.1

Reply via email to