diff mbox series

[13/15,v3] arm: Add support for dwarf debug directives and pseudo hard-register for PAC feature.

Message ID 8a8cd27c-d0e8-4a99-b39e-31889c2b805e@AZ-NEU-EX04.Arm.com
State New
Headers show
Series None | expand

Commit Message

Srinath Parvathaneni Aug. 19, 2022, 10:04 a.m. UTC
Hello,

This patch teaches the DWARF support in gcc about RA_AUTH_CODE pseudo hard-register and also 
.save {ra_auth_code} and .cfi_offset ra_auth_code <offset> dwarf directives for the PAC feature
in Armv8.1-M architecture.

RA_AUTH_CODE register number is 107 and it's dwarf register number is 143.

When compiled with " -march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb
-mfloat-abi=soft -fasynchronous-unwind-tables -g -O2 -S" command line options, the assembly
output after this patch looks like below:

        ...
        .cfi_startproc
        pacbti  ip, lr, sp
        movs    r1, #40
        push    {ip, lr}
        .save {ra_auth_code, lr}
        .cfi_def_cfa_offset 8
        .cfi_offset 143, -8
        .cfi_offset 14, -4
        ...
        pop     {ip, lr}
        .cfi_restore 14
        .cfi_restore 143
        .cfi_def_cfa_offset 0
        movs    r0, #0
        aut     ip, lr, sp
        bx      lr
        .cfi_endproc
        ...

Regression tested on arm-none-eabi target and found no regressions.

Ok for master?

Regards,
Srinath.

gcc/ChangeLog:

2022-08-17  Srinath Parvathaneni  <srinath.parvathaneni@arm.com>

        * config/arm/aout.h (ra_auth_code): Add to enum.
        * config/arm/arm.cc (emit_multi_reg_push): Add RA_AUTH_CODE register to
        dwarf frame expression.
        (arm_emit_multi_reg_pop): Restore RA_AUTH_CODE register.
        (arm_expand_prologue): Mark as frame related insn.
        (arm_regno_class): Check for pac pseudo reigster.
        (arm_dbx_register_number): Assign ra_auth_code register number in dwarf.
        (arm_unwind_emit_sequence): Print .save directive with ra_auth_code
        register.
        (arm_conditional_register_usage): Mark ra_auth_code in fixed reigsters.
        * config/arm/arm.h (FIRST_PSEUDO_REGISTER): Modify.
        (IS_PAC_Pseudo_REGNUM): Define.
        (enum reg_class): Add PAC_REG entry.
        * config/arm/arm.md (RA_AUTH_CODE): Define.

gcc/testsuite/ChangeLog:

2022-08-17  Srinath Parvathaneni  <srinath.parvathaneni@arm.com>

        * g++.target/arm/pac-1.C: New test.
        * gcc.target/arm/pac-9.c: Likewise.


###############     Attachment also inlined for ease of reply    ###############
diff --git a/gcc/config/arm/aout.h b/gcc/config/arm/aout.h
index b918ad3782fbee82320febb8b6e72ad615780261..ffeed45a678f17c63d5b42c21f020ca416cbf23f 100644
--- a/gcc/config/arm/aout.h
+++ b/gcc/config/arm/aout.h
@@ -74,7 +74,8 @@
   "wr8",   "wr9",   "wr10",  "wr11",				\
   "wr12",  "wr13",  "wr14",  "wr15",				\
   "wcgr0", "wcgr1", "wcgr2", "wcgr3",				\
-  "cc", "vfpcc", "sfp", "afp", "apsrq", "apsrge", "p0"		\
+  "cc", "vfpcc", "sfp", "afp", "apsrq", "apsrge", "p0",		\
+  "ra_auth_code"						\
 }
 #endif
 
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 3495ab857eac38ecdf37e55f1d201b1c35cbde0b..c77777067819f6785e44d30d8e5365505ab98682 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -816,7 +816,8 @@ extern const int arm_arch_cde_coproc_bits[];
 	s16-s31	      S	VFP variable (aka d8-d15).
 	vfpcc		Not a real register.  Represents the VFP condition
 			code flags.
-	vpr		Used to represent MVE VPR predication.  */
+	vpr		Used to represent MVE VPR predication.
+	ra_auth_code	Pseudo register to save PAC.  */
 
 /* The stack backtrace structure is as follows:
   fp points to here:  |  save code pointer  |      [fp]
@@ -857,7 +858,7 @@ extern const int arm_arch_cde_coproc_bits[];
   1,1,1,1,1,1,1,1,		\
   1,1,1,1,			\
   /* Specials.  */		\
-  1,1,1,1,1,1,1			\
+  1,1,1,1,1,1,1,1		\
 }
 
 /* 1 for registers not available across function calls.
@@ -887,7 +888,7 @@ extern const int arm_arch_cde_coproc_bits[];
   1,1,1,1,1,1,1,1,		\
   1,1,1,1,			\
   /* Specials.  */		\
-  1,1,1,1,1,1,1			\
+  1,1,1,1,1,1,1,1		\
 }
 
 #ifndef SUBTARGET_CONDITIONAL_REGISTER_USAGE
@@ -1063,10 +1064,10 @@ extern const int arm_arch_cde_coproc_bits[];
    && (LAST_VFP_REGNUM - (REGNUM) >= 2 * (N) - 1))
 
 /* The number of hard registers is 16 ARM + 1 CC + 1 SFP + 1 AFP
-   + 1 APSRQ + 1 APSRGE + 1 VPR.  */
+   + 1 APSRQ + 1 APSRGE + 1 VPR + 1 Pseudo register to save PAC.  */
 /* Intel Wireless MMX Technology registers add 16 + 4 more.  */
 /* VFP (VFP3) adds 32 (64) + 1 VFPCC.  */
-#define FIRST_PSEUDO_REGISTER   107
+#define FIRST_PSEUDO_REGISTER   108
 
 #define DBX_REGISTER_NUMBER(REGNO) arm_dbx_register_number (REGNO)
 
@@ -1253,12 +1254,15 @@ extern int arm_regs_in_sequence[];
   CC_REGNUM, VFPCC_REGNUM,			\
   FRAME_POINTER_REGNUM, ARG_POINTER_REGNUM,	\
   SP_REGNUM, PC_REGNUM, APSRQ_REGNUM,		\
-  APSRGE_REGNUM, VPR_REGNUM			\
+  APSRGE_REGNUM, VPR_REGNUM, RA_AUTH_CODE	\
 }
 
 #define IS_VPR_REGNUM(REGNUM) \
   ((REGNUM) == VPR_REGNUM)
 
+#define IS_PAC_Pseudo_REGNUM(REGNUM) \
+  ((REGNUM) == RA_AUTH_CODE)
+
 /* Use different register alloc ordering for Thumb.  */
 #define ADJUST_REG_ALLOC_ORDER arm_order_regs_for_local_alloc ()
 
@@ -1297,6 +1301,7 @@ enum reg_class
   SFP_REG,
   AFP_REG,
   VPR_REG,
+  PAC_REG,
   GENERAL_AND_VPR_REGS,
   ALL_REGS,
   LIM_REG_CLASSES
@@ -1327,6 +1332,7 @@ enum reg_class
   "SFP_REG",		\
   "AFP_REG",		\
   "VPR_REG",		\
+  "PAC_REG",		\
   "GENERAL_AND_VPR_REGS", \
   "ALL_REGS"		\
 }
@@ -1356,6 +1362,7 @@ enum reg_class
   { 0x00000000, 0x00000000, 0x00000000, 0x00000040 }, /* SFP_REG */	\
   { 0x00000000, 0x00000000, 0x00000000, 0x00000080 }, /* AFP_REG */	\
   { 0x00000000, 0x00000000, 0x00000000, 0x00000400 }, /* VPR_REG.  */	\
+  { 0x00000000, 0x00000000, 0x00000000, 0x00000800 }, /* PAC_REG.  */	\
   { 0x00005FFF, 0x00000000, 0x00000000, 0x00000400 }, /* GENERAL_AND_VPR_REGS.  */ \
   { 0xFFFF7FFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000040F }  /* ALL_REGS.  */	\
 }
diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index de5a679c92a4bf253a15a1ab9cd0ae99e7cd62ec..c1de53882c9db897cadffc37353ec3e2ff11b657 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -22148,7 +22148,9 @@ emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
     {
       if (mask & (1 << i))
 	{
-	  reg = gen_rtx_REG (SImode, i);
+	  rtx reg1 = reg = gen_rtx_REG (SImode, i);
+	  if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
+	    reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
 
 	  XVECEXP (par, 0, 0)
 	    = gen_rtx_SET (gen_frame_mem
@@ -22165,8 +22167,12 @@ emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
 
 	  if (dwarf_regs_mask & (1 << i))
 	    {
+	      /* Only the first register in the multi push instruction is stored
+		 to frame memory here.
+		Eg: push {r7 ,r8, ip, lr}
+		Only r7 is stored to frame memory here.  */
 	      tmp = gen_rtx_SET (gen_frame_mem (SImode, stack_pointer_rtx),
-				 reg);
+				 reg1);
 	      RTX_FRAME_RELATED_P (tmp) = 1;
 	      XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
 	    }
@@ -22179,18 +22185,25 @@ emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
     {
       if (mask & (1 << i))
 	{
-	  reg = gen_rtx_REG (SImode, i);
+	  rtx reg1 = reg = gen_rtx_REG (SImode, i);
+	  if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
+	    reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
 
 	  XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg);
 
 	  if (dwarf_regs_mask & (1 << i))
 	    {
+	      /* Except the first register in the multi push instruction all the
+		 remaining registers are stored to frame memory here.
+		 Eg: push {r7, r8, ip, lr}
+		 r8, ip (ra_auth_code in case PACBTI enabled) and lr registers
+		 are stored to frame memory here.  */
 	      tmp
 		= gen_rtx_SET (gen_frame_mem
 			       (SImode,
 				plus_constant (Pmode, stack_pointer_rtx,
 					       4 * j)),
-			       reg);
+			       reg1);
 	      RTX_FRAME_RELATED_P (tmp) = 1;
 	      XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
 	    }
@@ -22275,7 +22288,9 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
   for (j = 0, i = 0; j < num_regs; i++)
     if (saved_regs_mask & (1 << i))
       {
-        reg = gen_rtx_REG (SImode, i);
+	rtx reg1 = reg = gen_rtx_REG (SImode, i);
+	if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
+	  reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
         if ((num_regs == 1) && emit_update && !return_in_pc)
           {
             /* Emit single load with writeback.  */
@@ -22283,7 +22298,7 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
                                  gen_rtx_POST_INC (Pmode,
                                                    stack_pointer_rtx));
             tmp = emit_insn (gen_rtx_SET (reg, tmp));
-            REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+	    REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg1, dwarf);
             return;
           }
 
@@ -22297,7 +22312,7 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
         /* We need to maintain a sequence for DWARF info too.  As dwarf info
            should not have PC, skip PC.  */
         if (i != PC_REGNUM)
-          dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+	  dwarf = alloc_reg_note (REG_CFA_RESTORE, reg1, dwarf);
 
         j++;
       }
@@ -25592,6 +25607,9 @@ arm_regno_class (int regno)
   if (IS_VPR_REGNUM (regno))
     return VPR_REG;
 
+  if (IS_PAC_Pseudo_REGNUM (regno))
+    return PAC_REG;
+
   if (TARGET_THUMB1)
     {
       if (regno == STACK_POINTER_REGNUM)
@@ -29563,6 +29581,9 @@ arm_dbx_register_number (unsigned int regno)
   if (IS_IWMMXT_REGNUM (regno))
     return 112 + regno - FIRST_IWMMXT_REGNUM;
 
+  if (IS_PAC_Pseudo_REGNUM (regno))
+    return 143;
+
   return DWARF_FRAME_REGISTERS;
 }
 
@@ -29656,7 +29677,7 @@ arm_unwind_emit_sequence (FILE * out_file, rtx p)
   gcc_assert (nregs);
 
   reg = REGNO (SET_SRC (XVECEXP (p, 0, 1)));
-  if (reg < 16)
+  if (reg < 16 || IS_PAC_Pseudo_REGNUM (reg))
     {
       /* For -Os dummy registers can be pushed at the beginning to
 	 avoid separate stack pointer adjustment.  */
@@ -29713,6 +29734,8 @@ arm_unwind_emit_sequence (FILE * out_file, rtx p)
 	 double precision register names.  */
       if (IS_VFP_REGNUM (reg))
 	asm_fprintf (out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2);
+      else if (IS_PAC_Pseudo_REGNUM (reg))
+	asm_fprintf (asm_out_file, "ra_auth_code");
       else
 	asm_fprintf (out_file, "%r", reg);
 
@@ -30580,6 +30603,9 @@ arm_conditional_register_usage (void)
 	global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
     }
 
+  if (TARGET_HAVE_PACBTI)
+    fixed_regs[RA_AUTH_CODE] = 0;
+
   /* The Q and GE bits are only accessed via special ACLE patterns.  */
   CLEAR_HARD_REG_BIT (operand_reg_set, APSRQ_REGNUM);
   CLEAR_HARD_REG_BIT (operand_reg_set, APSRGE_REGNUM);
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 90c8c1d66f57e8cd2eca4ef63f2100c04c5c5982..96079e7d760823af46b86978e3d7dd2a0ba5ed29 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -42,6 +42,7 @@
    (APSRQ_REGNUM    104)	; Q bit pseudo register
    (APSRGE_REGNUM   105)	; GE bits pseudo register
    (VPR_REGNUM      106)	; Vector Predication Register - MVE register.
+   (RA_AUTH_CODE    107)	; Pseudo register to save PAC.
   ]
 )
 ;; 3rd operand to select_dominance_cc_mode
diff --git a/gcc/testsuite/g++.target/arm/pac-1.C b/gcc/testsuite/g++.target/arm/pac-1.C
new file mode 100644
index 0000000000000000000000000000000000000000..b447213ab4e1b72cacd483efece077fbbca3a608
--- /dev/null
+++ b/gcc/testsuite/g++.target/arm/pac-1.C
@@ -0,0 +1,33 @@
+/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register.  */
+/* { dg-do compile } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb -mfloat-abi=soft --save-temps -g" } */
+
+__attribute__((noinline)) void
+fn1 (int a, int b, int c)
+{
+  if (a != b + c)
+    __builtin_abort ();
+  else
+    throw b+c;
+}
+
+int main ()
+{
+  int a = 120;
+  try
+    {
+      fn1 (a, 40, 80);
+    }
+  catch (int x)
+    {
+      if (x != a)
+        __builtin_abort ();
+      else
+	return 0;
+    }
+}
+
+/* { dg-final { scan-assembler "\.save \{r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.save \{r4, r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.cfi_offset 143, \-8" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-9.c b/gcc/testsuite/gcc.target/arm/pac-9.c
new file mode 100644
index 0000000000000000000000000000000000000000..2a69db6e8e8a32090a6c16ccc66871946188f8d8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-9.c
@@ -0,0 +1,21 @@
+/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register.  */
+/* { dg-do compile } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb -mfloat-abi=soft -fasynchronous-unwind-tables --save-temps -g" } */
+
+__attribute__((noinline)) void
+fn1 (int a, int b, int c)
+{
+  if (a != b + c)
+    __builtin_abort ();
+}
+
+int main ()
+{
+  fn1 (40, 40, 80);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "\.save \{r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.save \{r3, r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.cfi_offset 143, \-8" } } */

Comments

Richard Earnshaw Aug. 19, 2022, 11:23 a.m. UTC | #1
On 19/08/2022 11:04, Srinath Parvathaneni via Gcc-patches wrote:
> Hello,
> 
> This patch teaches the DWARF support in gcc about RA_AUTH_CODE pseudo hard-register and also
> .save {ra_auth_code} and .cfi_offset ra_auth_code <offset> dwarf directives for the PAC feature
> in Armv8.1-M architecture.
> 
> RA_AUTH_CODE register number is 107 and it's dwarf register number is 143.
> 
> When compiled with " -march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb
> -mfloat-abi=soft -fasynchronous-unwind-tables -g -O2 -S" command line options, the assembly
> output after this patch looks like below:
> 
>          ...
>          .cfi_startproc
>          pacbti  ip, lr, sp
>          movs    r1, #40
>          push    {ip, lr}
>          .save {ra_auth_code, lr}
>          .cfi_def_cfa_offset 8
>          .cfi_offset 143, -8
>          .cfi_offset 14, -4
>          ...
>          pop     {ip, lr}
>          .cfi_restore 14
>          .cfi_restore 143
>          .cfi_def_cfa_offset 0
>          movs    r0, #0
>          aut     ip, lr, sp
>          bx      lr
>          .cfi_endproc
>          ...
> 
> Regression tested on arm-none-eabi target and found no regressions.
> 
> Ok for master?
> 
> Regards,
> Srinath.
> 
> gcc/ChangeLog:
> 
> 2022-08-17  Srinath Parvathaneni  <srinath.parvathaneni@arm.com>
> 
>          * config/arm/aout.h (ra_auth_code): Add to enum.
>          * config/arm/arm.cc (emit_multi_reg_push): Add RA_AUTH_CODE register to
>          dwarf frame expression.
>          (arm_emit_multi_reg_pop): Restore RA_AUTH_CODE register.
>          (arm_expand_prologue): Mark as frame related insn.
>          (arm_regno_class): Check for pac pseudo reigster.
>          (arm_dbx_register_number): Assign ra_auth_code register number in dwarf.
>          (arm_unwind_emit_sequence): Print .save directive with ra_auth_code
>          register.
>          (arm_conditional_register_usage): Mark ra_auth_code in fixed reigsters.
>          * config/arm/arm.h (FIRST_PSEUDO_REGISTER): Modify.
>          (IS_PAC_Pseudo_REGNUM): Define.
>          (enum reg_class): Add PAC_REG entry.
>          * config/arm/arm.md (RA_AUTH_CODE): Define.
> 
> gcc/testsuite/ChangeLog:
> 
> 2022-08-17  Srinath Parvathaneni  <srinath.parvathaneni@arm.com>
> 
>          * g++.target/arm/pac-1.C: New test.
>          * gcc.target/arm/pac-9.c: Likewise.
> 
The general boiler-plate to add the RA register is OK, but the code that 
tweaks the generation of the push instructions is fixing the wrong 
problem.  The dwarf code already knows how to to track reg->reg copies 
and put out the right frame information, but this isn't working because 
you've not augmented the PAC instruction correctly.  What you need is a 
frame-related augmentation to that and that essentially does

	(set (IP_REGNUM) (RA_AUTH_CODE))

The generic dwarf code should then handle all the rest for emitting CFI 
directives.

The code in arm_unwind_emit_sequence (and technically in 
arm_unwind_emit_set as well, but we probably never reach that code as of 
today) then needs updating to handle the special cases when IP appears 
in the list of registers and PAC is enabled.  That's a bit of a hack, 
but I can't immediately think of a better way of handling it.

R.

> 
> ###############     Attachment also inlined for ease of reply    ###############
> 
> 
> diff --git a/gcc/config/arm/aout.h b/gcc/config/arm/aout.h
> index b918ad3782fbee82320febb8b6e72ad615780261..ffeed45a678f17c63d5b42c21f020ca416cbf23f 100644
> --- a/gcc/config/arm/aout.h
> +++ b/gcc/config/arm/aout.h
> @@ -74,7 +74,8 @@
>     "wr8",   "wr9",   "wr10",  "wr11",				\
>     "wr12",  "wr13",  "wr14",  "wr15",				\
>     "wcgr0", "wcgr1", "wcgr2", "wcgr3",				\
> -  "cc", "vfpcc", "sfp", "afp", "apsrq", "apsrge", "p0"		\
> +  "cc", "vfpcc", "sfp", "afp", "apsrq", "apsrge", "p0",		\
> +  "ra_auth_code"						\
>   }
>   #endif
>   
> diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
> index 3495ab857eac38ecdf37e55f1d201b1c35cbde0b..c77777067819f6785e44d30d8e5365505ab98682 100644
> --- a/gcc/config/arm/arm.h
> +++ b/gcc/config/arm/arm.h
> @@ -816,7 +816,8 @@ extern const int arm_arch_cde_coproc_bits[];
>   	s16-s31	      S	VFP variable (aka d8-d15).
>   	vfpcc		Not a real register.  Represents the VFP condition
>   			code flags.
> -	vpr		Used to represent MVE VPR predication.  */
> +	vpr		Used to represent MVE VPR predication.
> +	ra_auth_code	Pseudo register to save PAC.  */
>   
>   /* The stack backtrace structure is as follows:
>     fp points to here:  |  save code pointer  |      [fp]
> @@ -857,7 +858,7 @@ extern const int arm_arch_cde_coproc_bits[];
>     1,1,1,1,1,1,1,1,		\
>     1,1,1,1,			\
>     /* Specials.  */		\
> -  1,1,1,1,1,1,1			\
> +  1,1,1,1,1,1,1,1		\
>   }
>   
>   /* 1 for registers not available across function calls.
> @@ -887,7 +888,7 @@ extern const int arm_arch_cde_coproc_bits[];
>     1,1,1,1,1,1,1,1,		\
>     1,1,1,1,			\
>     /* Specials.  */		\
> -  1,1,1,1,1,1,1			\
> +  1,1,1,1,1,1,1,1		\
>   }
>   
>   #ifndef SUBTARGET_CONDITIONAL_REGISTER_USAGE
> @@ -1063,10 +1064,10 @@ extern const int arm_arch_cde_coproc_bits[];
>      && (LAST_VFP_REGNUM - (REGNUM) >= 2 * (N) - 1))
>   
>   /* The number of hard registers is 16 ARM + 1 CC + 1 SFP + 1 AFP
> -   + 1 APSRQ + 1 APSRGE + 1 VPR.  */
> +   + 1 APSRQ + 1 APSRGE + 1 VPR + 1 Pseudo register to save PAC.  */
>   /* Intel Wireless MMX Technology registers add 16 + 4 more.  */
>   /* VFP (VFP3) adds 32 (64) + 1 VFPCC.  */
> -#define FIRST_PSEUDO_REGISTER   107
> +#define FIRST_PSEUDO_REGISTER   108
>   
>   #define DBX_REGISTER_NUMBER(REGNO) arm_dbx_register_number (REGNO)
>   
> @@ -1253,12 +1254,15 @@ extern int arm_regs_in_sequence[];
>     CC_REGNUM, VFPCC_REGNUM,			\
>     FRAME_POINTER_REGNUM, ARG_POINTER_REGNUM,	\
>     SP_REGNUM, PC_REGNUM, APSRQ_REGNUM,		\
> -  APSRGE_REGNUM, VPR_REGNUM			\
> +  APSRGE_REGNUM, VPR_REGNUM, RA_AUTH_CODE	\
>   }
>   
>   #define IS_VPR_REGNUM(REGNUM) \
>     ((REGNUM) == VPR_REGNUM)
>   
> +#define IS_PAC_Pseudo_REGNUM(REGNUM) \
> +  ((REGNUM) == RA_AUTH_CODE)
> +
>   /* Use different register alloc ordering for Thumb.  */
>   #define ADJUST_REG_ALLOC_ORDER arm_order_regs_for_local_alloc ()
>   
> @@ -1297,6 +1301,7 @@ enum reg_class
>     SFP_REG,
>     AFP_REG,
>     VPR_REG,
> +  PAC_REG,
>     GENERAL_AND_VPR_REGS,
>     ALL_REGS,
>     LIM_REG_CLASSES
> @@ -1327,6 +1332,7 @@ enum reg_class
>     "SFP_REG",		\
>     "AFP_REG",		\
>     "VPR_REG",		\
> +  "PAC_REG",		\
>     "GENERAL_AND_VPR_REGS", \
>     "ALL_REGS"		\
>   }
> @@ -1356,6 +1362,7 @@ enum reg_class
>     { 0x00000000, 0x00000000, 0x00000000, 0x00000040 }, /* SFP_REG */	\
>     { 0x00000000, 0x00000000, 0x00000000, 0x00000080 }, /* AFP_REG */	\
>     { 0x00000000, 0x00000000, 0x00000000, 0x00000400 }, /* VPR_REG.  */	\
> +  { 0x00000000, 0x00000000, 0x00000000, 0x00000800 }, /* PAC_REG.  */	\
>     { 0x00005FFF, 0x00000000, 0x00000000, 0x00000400 }, /* GENERAL_AND_VPR_REGS.  */ \
>     { 0xFFFF7FFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000040F }  /* ALL_REGS.  */	\
>   }
> diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
> index de5a679c92a4bf253a15a1ab9cd0ae99e7cd62ec..c1de53882c9db897cadffc37353ec3e2ff11b657 100644
> --- a/gcc/config/arm/arm.cc
> +++ b/gcc/config/arm/arm.cc
> @@ -22148,7 +22148,9 @@ emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
>       {
>         if (mask & (1 << i))
>   	{
> -	  reg = gen_rtx_REG (SImode, i);
> +	  rtx reg1 = reg = gen_rtx_REG (SImode, i);
> +	  if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
> +	    reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
>   
>   	  XVECEXP (par, 0, 0)
>   	    = gen_rtx_SET (gen_frame_mem
> @@ -22165,8 +22167,12 @@ emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
>   
>   	  if (dwarf_regs_mask & (1 << i))
>   	    {
> +	      /* Only the first register in the multi push instruction is stored
> +		 to frame memory here.
> +		Eg: push {r7 ,r8, ip, lr}
> +		Only r7 is stored to frame memory here.  */
>   	      tmp = gen_rtx_SET (gen_frame_mem (SImode, stack_pointer_rtx),
> -				 reg);
> +				 reg1);
>   	      RTX_FRAME_RELATED_P (tmp) = 1;
>   	      XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
>   	    }
> @@ -22179,18 +22185,25 @@ emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
>       {
>         if (mask & (1 << i))
>   	{
> -	  reg = gen_rtx_REG (SImode, i);
> +	  rtx reg1 = reg = gen_rtx_REG (SImode, i);
> +	  if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
> +	    reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
>   
>   	  XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg);
>   
>   	  if (dwarf_regs_mask & (1 << i))
>   	    {
> +	      /* Except the first register in the multi push instruction all the
> +		 remaining registers are stored to frame memory here.
> +		 Eg: push {r7, r8, ip, lr}
> +		 r8, ip (ra_auth_code in case PACBTI enabled) and lr registers
> +		 are stored to frame memory here.  */
>   	      tmp
>   		= gen_rtx_SET (gen_frame_mem
>   			       (SImode,
>   				plus_constant (Pmode, stack_pointer_rtx,
>   					       4 * j)),
> -			       reg);
> +			       reg1);
>   	      RTX_FRAME_RELATED_P (tmp) = 1;
>   	      XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
>   	    }
> @@ -22275,7 +22288,9 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
>     for (j = 0, i = 0; j < num_regs; i++)
>       if (saved_regs_mask & (1 << i))
>         {
> -        reg = gen_rtx_REG (SImode, i);
> +	rtx reg1 = reg = gen_rtx_REG (SImode, i);
> +	if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
> +	  reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
>           if ((num_regs == 1) && emit_update && !return_in_pc)
>             {
>               /* Emit single load with writeback.  */
> @@ -22283,7 +22298,7 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
>                                    gen_rtx_POST_INC (Pmode,
>                                                      stack_pointer_rtx));
>               tmp = emit_insn (gen_rtx_SET (reg, tmp));
> -            REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
> +	    REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg1, dwarf);
>               return;
>             }
>   
> @@ -22297,7 +22312,7 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
>           /* We need to maintain a sequence for DWARF info too.  As dwarf info
>              should not have PC, skip PC.  */
>           if (i != PC_REGNUM)
> -          dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
> +	  dwarf = alloc_reg_note (REG_CFA_RESTORE, reg1, dwarf);
>   
>           j++;
>         }
> @@ -25592,6 +25607,9 @@ arm_regno_class (int regno)
>     if (IS_VPR_REGNUM (regno))
>       return VPR_REG;
>   
> +  if (IS_PAC_Pseudo_REGNUM (regno))
> +    return PAC_REG;
> +
>     if (TARGET_THUMB1)
>       {
>         if (regno == STACK_POINTER_REGNUM)
> @@ -29563,6 +29581,9 @@ arm_dbx_register_number (unsigned int regno)
>     if (IS_IWMMXT_REGNUM (regno))
>       return 112 + regno - FIRST_IWMMXT_REGNUM;
>   
> +  if (IS_PAC_Pseudo_REGNUM (regno))
> +    return 143;
> +
>     return DWARF_FRAME_REGISTERS;
>   }
>   
> @@ -29656,7 +29677,7 @@ arm_unwind_emit_sequence (FILE * out_file, rtx p)
>     gcc_assert (nregs);
>   
>     reg = REGNO (SET_SRC (XVECEXP (p, 0, 1)));
> -  if (reg < 16)
> +  if (reg < 16 || IS_PAC_Pseudo_REGNUM (reg))
>       {
>         /* For -Os dummy registers can be pushed at the beginning to
>   	 avoid separate stack pointer adjustment.  */
> @@ -29713,6 +29734,8 @@ arm_unwind_emit_sequence (FILE * out_file, rtx p)
>   	 double precision register names.  */
>         if (IS_VFP_REGNUM (reg))
>   	asm_fprintf (out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2);
> +      else if (IS_PAC_Pseudo_REGNUM (reg))
> +	asm_fprintf (asm_out_file, "ra_auth_code");
>         else
>   	asm_fprintf (out_file, "%r", reg);
>   
> @@ -30580,6 +30603,9 @@ arm_conditional_register_usage (void)
>   	global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
>       }
>   
> +  if (TARGET_HAVE_PACBTI)
> +    fixed_regs[RA_AUTH_CODE] = 0;
> +
>     /* The Q and GE bits are only accessed via special ACLE patterns.  */
>     CLEAR_HARD_REG_BIT (operand_reg_set, APSRQ_REGNUM);
>     CLEAR_HARD_REG_BIT (operand_reg_set, APSRGE_REGNUM);
> diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
> index 90c8c1d66f57e8cd2eca4ef63f2100c04c5c5982..96079e7d760823af46b86978e3d7dd2a0ba5ed29 100644
> --- a/gcc/config/arm/arm.md
> +++ b/gcc/config/arm/arm.md
> @@ -42,6 +42,7 @@
>      (APSRQ_REGNUM    104)	; Q bit pseudo register
>      (APSRGE_REGNUM   105)	; GE bits pseudo register
>      (VPR_REGNUM      106)	; Vector Predication Register - MVE register.
> +   (RA_AUTH_CODE    107)	; Pseudo register to save PAC.
>     ]
>   )
>   ;; 3rd operand to select_dominance_cc_mode
> diff --git a/gcc/testsuite/g++.target/arm/pac-1.C b/gcc/testsuite/g++.target/arm/pac-1.C
> new file mode 100644
> index 0000000000000000000000000000000000000000..b447213ab4e1b72cacd483efece077fbbca3a608
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/arm/pac-1.C
> @@ -0,0 +1,33 @@
> +/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register.  */
> +/* { dg-do compile } */
> +/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
> +/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb -mfloat-abi=soft --save-temps -g" } */
> +
> +__attribute__((noinline)) void
> +fn1 (int a, int b, int c)
> +{
> +  if (a != b + c)
> +    __builtin_abort ();
> +  else
> +    throw b+c;
> +}
> +
> +int main ()
> +{
> +  int a = 120;
> +  try
> +    {
> +      fn1 (a, 40, 80);
> +    }
> +  catch (int x)
> +    {
> +      if (x != a)
> +        __builtin_abort ();
> +      else
> +	return 0;
> +    }
> +}
> +
> +/* { dg-final { scan-assembler "\.save \{r7, ra_auth_code, lr\}" } } */
> +/* { dg-final { scan-assembler "\.save \{r4, r7, ra_auth_code, lr\}" } } */
> +/* { dg-final { scan-assembler "\.cfi_offset 143, \-8" } } */
> diff --git a/gcc/testsuite/gcc.target/arm/pac-9.c b/gcc/testsuite/gcc.target/arm/pac-9.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..2a69db6e8e8a32090a6c16ccc66871946188f8d8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/pac-9.c
> @@ -0,0 +1,21 @@
> +/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register.  */
> +/* { dg-do compile } */
> +/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
> +/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb -mfloat-abi=soft -fasynchronous-unwind-tables --save-temps -g" } */
> +
> +__attribute__((noinline)) void
> +fn1 (int a, int b, int c)
> +{
> +  if (a != b + c)
> +    __builtin_abort ();
> +}
> +
> +int main ()
> +{
> +  fn1 (40, 40, 80);
> +  return 0;
> +}
> +
> +/* { dg-final { scan-assembler "\.save \{r7, ra_auth_code, lr\}" } } */
> +/* { dg-final { scan-assembler "\.save \{r3, r7, ra_auth_code, lr\}" } } */
> +/* { dg-final { scan-assembler "\.cfi_offset 143, \-8" } } */
> 
> 
>
diff mbox series

Patch

diff --git a/gcc/config/arm/aout.h b/gcc/config/arm/aout.h
index b918ad3782fbee82320febb8b6e72ad615780261..ffeed45a678f17c63d5b42c21f020ca416cbf23f 100644
--- a/gcc/config/arm/aout.h
+++ b/gcc/config/arm/aout.h
@@ -74,7 +74,8 @@ 
   "wr8",   "wr9",   "wr10",  "wr11",				\
   "wr12",  "wr13",  "wr14",  "wr15",				\
   "wcgr0", "wcgr1", "wcgr2", "wcgr3",				\
-  "cc", "vfpcc", "sfp", "afp", "apsrq", "apsrge", "p0"		\
+  "cc", "vfpcc", "sfp", "afp", "apsrq", "apsrge", "p0",		\
+  "ra_auth_code"						\
 }
 #endif
 
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 3495ab857eac38ecdf37e55f1d201b1c35cbde0b..c77777067819f6785e44d30d8e5365505ab98682 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -816,7 +816,8 @@  extern const int arm_arch_cde_coproc_bits[];
 	s16-s31	      S	VFP variable (aka d8-d15).
 	vfpcc		Not a real register.  Represents the VFP condition
 			code flags.
-	vpr		Used to represent MVE VPR predication.  */
+	vpr		Used to represent MVE VPR predication.
+	ra_auth_code	Pseudo register to save PAC.  */
 
 /* The stack backtrace structure is as follows:
   fp points to here:  |  save code pointer  |      [fp]
@@ -857,7 +858,7 @@  extern const int arm_arch_cde_coproc_bits[];
   1,1,1,1,1,1,1,1,		\
   1,1,1,1,			\
   /* Specials.  */		\
-  1,1,1,1,1,1,1			\
+  1,1,1,1,1,1,1,1		\
 }
 
 /* 1 for registers not available across function calls.
@@ -887,7 +888,7 @@  extern const int arm_arch_cde_coproc_bits[];
   1,1,1,1,1,1,1,1,		\
   1,1,1,1,			\
   /* Specials.  */		\
-  1,1,1,1,1,1,1			\
+  1,1,1,1,1,1,1,1		\
 }
 
 #ifndef SUBTARGET_CONDITIONAL_REGISTER_USAGE
@@ -1063,10 +1064,10 @@  extern const int arm_arch_cde_coproc_bits[];
    && (LAST_VFP_REGNUM - (REGNUM) >= 2 * (N) - 1))
 
 /* The number of hard registers is 16 ARM + 1 CC + 1 SFP + 1 AFP
-   + 1 APSRQ + 1 APSRGE + 1 VPR.  */
+   + 1 APSRQ + 1 APSRGE + 1 VPR + 1 Pseudo register to save PAC.  */
 /* Intel Wireless MMX Technology registers add 16 + 4 more.  */
 /* VFP (VFP3) adds 32 (64) + 1 VFPCC.  */
-#define FIRST_PSEUDO_REGISTER   107
+#define FIRST_PSEUDO_REGISTER   108
 
 #define DBX_REGISTER_NUMBER(REGNO) arm_dbx_register_number (REGNO)
 
@@ -1253,12 +1254,15 @@  extern int arm_regs_in_sequence[];
   CC_REGNUM, VFPCC_REGNUM,			\
   FRAME_POINTER_REGNUM, ARG_POINTER_REGNUM,	\
   SP_REGNUM, PC_REGNUM, APSRQ_REGNUM,		\
-  APSRGE_REGNUM, VPR_REGNUM			\
+  APSRGE_REGNUM, VPR_REGNUM, RA_AUTH_CODE	\
 }
 
 #define IS_VPR_REGNUM(REGNUM) \
   ((REGNUM) == VPR_REGNUM)
 
+#define IS_PAC_Pseudo_REGNUM(REGNUM) \
+  ((REGNUM) == RA_AUTH_CODE)
+
 /* Use different register alloc ordering for Thumb.  */
 #define ADJUST_REG_ALLOC_ORDER arm_order_regs_for_local_alloc ()
 
@@ -1297,6 +1301,7 @@  enum reg_class
   SFP_REG,
   AFP_REG,
   VPR_REG,
+  PAC_REG,
   GENERAL_AND_VPR_REGS,
   ALL_REGS,
   LIM_REG_CLASSES
@@ -1327,6 +1332,7 @@  enum reg_class
   "SFP_REG",		\
   "AFP_REG",		\
   "VPR_REG",		\
+  "PAC_REG",		\
   "GENERAL_AND_VPR_REGS", \
   "ALL_REGS"		\
 }
@@ -1356,6 +1362,7 @@  enum reg_class
   { 0x00000000, 0x00000000, 0x00000000, 0x00000040 }, /* SFP_REG */	\
   { 0x00000000, 0x00000000, 0x00000000, 0x00000080 }, /* AFP_REG */	\
   { 0x00000000, 0x00000000, 0x00000000, 0x00000400 }, /* VPR_REG.  */	\
+  { 0x00000000, 0x00000000, 0x00000000, 0x00000800 }, /* PAC_REG.  */	\
   { 0x00005FFF, 0x00000000, 0x00000000, 0x00000400 }, /* GENERAL_AND_VPR_REGS.  */ \
   { 0xFFFF7FFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000040F }  /* ALL_REGS.  */	\
 }
diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index de5a679c92a4bf253a15a1ab9cd0ae99e7cd62ec..c1de53882c9db897cadffc37353ec3e2ff11b657 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -22148,7 +22148,9 @@  emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
     {
       if (mask & (1 << i))
 	{
-	  reg = gen_rtx_REG (SImode, i);
+	  rtx reg1 = reg = gen_rtx_REG (SImode, i);
+	  if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
+	    reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
 
 	  XVECEXP (par, 0, 0)
 	    = gen_rtx_SET (gen_frame_mem
@@ -22165,8 +22167,12 @@  emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
 
 	  if (dwarf_regs_mask & (1 << i))
 	    {
+	      /* Only the first register in the multi push instruction is stored
+		 to frame memory here.
+		Eg: push {r7 ,r8, ip, lr}
+		Only r7 is stored to frame memory here.  */
 	      tmp = gen_rtx_SET (gen_frame_mem (SImode, stack_pointer_rtx),
-				 reg);
+				 reg1);
 	      RTX_FRAME_RELATED_P (tmp) = 1;
 	      XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
 	    }
@@ -22179,18 +22185,25 @@  emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
     {
       if (mask & (1 << i))
 	{
-	  reg = gen_rtx_REG (SImode, i);
+	  rtx reg1 = reg = gen_rtx_REG (SImode, i);
+	  if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
+	    reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
 
 	  XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg);
 
 	  if (dwarf_regs_mask & (1 << i))
 	    {
+	      /* Except the first register in the multi push instruction all the
+		 remaining registers are stored to frame memory here.
+		 Eg: push {r7, r8, ip, lr}
+		 r8, ip (ra_auth_code in case PACBTI enabled) and lr registers
+		 are stored to frame memory here.  */
 	      tmp
 		= gen_rtx_SET (gen_frame_mem
 			       (SImode,
 				plus_constant (Pmode, stack_pointer_rtx,
 					       4 * j)),
-			       reg);
+			       reg1);
 	      RTX_FRAME_RELATED_P (tmp) = 1;
 	      XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
 	    }
@@ -22275,7 +22288,9 @@  arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
   for (j = 0, i = 0; j < num_regs; i++)
     if (saved_regs_mask & (1 << i))
       {
-        reg = gen_rtx_REG (SImode, i);
+	rtx reg1 = reg = gen_rtx_REG (SImode, i);
+	if (arm_current_function_pac_enabled_p () && i == IP_REGNUM)
+	  reg1 = gen_rtx_REG (SImode, RA_AUTH_CODE);
         if ((num_regs == 1) && emit_update && !return_in_pc)
           {
             /* Emit single load with writeback.  */
@@ -22283,7 +22298,7 @@  arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
                                  gen_rtx_POST_INC (Pmode,
                                                    stack_pointer_rtx));
             tmp = emit_insn (gen_rtx_SET (reg, tmp));
-            REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+	    REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg1, dwarf);
             return;
           }
 
@@ -22297,7 +22312,7 @@  arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
         /* We need to maintain a sequence for DWARF info too.  As dwarf info
            should not have PC, skip PC.  */
         if (i != PC_REGNUM)
-          dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+	  dwarf = alloc_reg_note (REG_CFA_RESTORE, reg1, dwarf);
 
         j++;
       }
@@ -25592,6 +25607,9 @@  arm_regno_class (int regno)
   if (IS_VPR_REGNUM (regno))
     return VPR_REG;
 
+  if (IS_PAC_Pseudo_REGNUM (regno))
+    return PAC_REG;
+
   if (TARGET_THUMB1)
     {
       if (regno == STACK_POINTER_REGNUM)
@@ -29563,6 +29581,9 @@  arm_dbx_register_number (unsigned int regno)
   if (IS_IWMMXT_REGNUM (regno))
     return 112 + regno - FIRST_IWMMXT_REGNUM;
 
+  if (IS_PAC_Pseudo_REGNUM (regno))
+    return 143;
+
   return DWARF_FRAME_REGISTERS;
 }
 
@@ -29656,7 +29677,7 @@  arm_unwind_emit_sequence (FILE * out_file, rtx p)
   gcc_assert (nregs);
 
   reg = REGNO (SET_SRC (XVECEXP (p, 0, 1)));
-  if (reg < 16)
+  if (reg < 16 || IS_PAC_Pseudo_REGNUM (reg))
     {
       /* For -Os dummy registers can be pushed at the beginning to
 	 avoid separate stack pointer adjustment.  */
@@ -29713,6 +29734,8 @@  arm_unwind_emit_sequence (FILE * out_file, rtx p)
 	 double precision register names.  */
       if (IS_VFP_REGNUM (reg))
 	asm_fprintf (out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2);
+      else if (IS_PAC_Pseudo_REGNUM (reg))
+	asm_fprintf (asm_out_file, "ra_auth_code");
       else
 	asm_fprintf (out_file, "%r", reg);
 
@@ -30580,6 +30603,9 @@  arm_conditional_register_usage (void)
 	global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
     }
 
+  if (TARGET_HAVE_PACBTI)
+    fixed_regs[RA_AUTH_CODE] = 0;
+
   /* The Q and GE bits are only accessed via special ACLE patterns.  */
   CLEAR_HARD_REG_BIT (operand_reg_set, APSRQ_REGNUM);
   CLEAR_HARD_REG_BIT (operand_reg_set, APSRGE_REGNUM);
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 90c8c1d66f57e8cd2eca4ef63f2100c04c5c5982..96079e7d760823af46b86978e3d7dd2a0ba5ed29 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -42,6 +42,7 @@ 
    (APSRQ_REGNUM    104)	; Q bit pseudo register
    (APSRGE_REGNUM   105)	; GE bits pseudo register
    (VPR_REGNUM      106)	; Vector Predication Register - MVE register.
+   (RA_AUTH_CODE    107)	; Pseudo register to save PAC.
   ]
 )
 ;; 3rd operand to select_dominance_cc_mode
diff --git a/gcc/testsuite/g++.target/arm/pac-1.C b/gcc/testsuite/g++.target/arm/pac-1.C
new file mode 100644
index 0000000000000000000000000000000000000000..b447213ab4e1b72cacd483efece077fbbca3a608
--- /dev/null
+++ b/gcc/testsuite/g++.target/arm/pac-1.C
@@ -0,0 +1,33 @@ 
+/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register.  */
+/* { dg-do compile } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb -mfloat-abi=soft --save-temps -g" } */
+
+__attribute__((noinline)) void
+fn1 (int a, int b, int c)
+{
+  if (a != b + c)
+    __builtin_abort ();
+  else
+    throw b+c;
+}
+
+int main ()
+{
+  int a = 120;
+  try
+    {
+      fn1 (a, 40, 80);
+    }
+  catch (int x)
+    {
+      if (x != a)
+        __builtin_abort ();
+      else
+	return 0;
+    }
+}
+
+/* { dg-final { scan-assembler "\.save \{r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.save \{r4, r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.cfi_offset 143, \-8" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-9.c b/gcc/testsuite/gcc.target/arm/pac-9.c
new file mode 100644
index 0000000000000000000000000000000000000000..2a69db6e8e8a32090a6c16ccc66871946188f8d8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-9.c
@@ -0,0 +1,21 @@ 
+/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register.  */
+/* { dg-do compile } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf+bti -mthumb -mfloat-abi=soft -fasynchronous-unwind-tables --save-temps -g" } */
+
+__attribute__((noinline)) void
+fn1 (int a, int b, int c)
+{
+  if (a != b + c)
+    __builtin_abort ();
+}
+
+int main ()
+{
+  fn1 (40, 40, 80);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "\.save \{r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.save \{r3, r7, ra_auth_code, lr\}" } } */
+/* { dg-final { scan-assembler "\.cfi_offset 143, \-8" } } */