diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index cff7ff1da2a..84764bf27ce 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -379,6 +379,7 @@ extern int vfp3_const_double_for_bits (rtx);
 extern void arm_emit_coreregs_64bit_shift (enum rtx_code, rtx, rtx, rtx, rtx,
 					   rtx);
 extern bool arm_fusion_enabled_p (tune_params::fuse_ops);
+extern bool arm_current_function_pac_enabled_p (void);
 extern bool arm_valid_symbolic_address_p (rtx);
 extern bool arm_validize_comparison (rtx *, rtx *, rtx *);
 extern bool arm_expand_vector_compare (rtx, rtx_code, rtx, rtx, bool);
diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index a5cf4225aa2..be5229ffd1d 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -927,6 +927,11 @@ int arm_arch8_3 = 0;
 
 /* Nonzero if this chip supports the ARM Architecture 8.4 extensions.  */
 int arm_arch8_4 = 0;
+
+/* Nonzero if this chip supports the ARM Architecture 8-M Mainline
+   extensions.  */
+int arm_arch8m_main = 0;
+
 /* Nonzero if this chip supports the ARM Architecture 8.1-M Mainline
    extensions.  */
 int arm_arch8_1m_main = 0;
@@ -3209,6 +3214,9 @@ arm_option_override_internal (struct gcc_options *opts,
       arm_stack_protector_guard_offset = offs;
     }
 
+  if (arm_current_function_pac_enabled_p () && !arm_arch8m_main)
+    error ("This architecture does not support branch protection instructions");
+
 #ifdef SUBTARGET_OVERRIDE_INTERNAL_OPTIONS
   SUBTARGET_OVERRIDE_INTERNAL_OPTIONS;
 #endif
@@ -3855,6 +3863,7 @@ arm_option_reconfigure_globals (void)
   arm_arch_arm_hwdiv = bitmap_bit_p (arm_active_target.isa, isa_bit_adiv);
   arm_arch_crc = bitmap_bit_p (arm_active_target.isa, isa_bit_crc32);
   arm_arch_cmse = bitmap_bit_p (arm_active_target.isa, isa_bit_cmse);
+  arm_arch8m_main = arm_arch7 && arm_arch_cmse;
   arm_arch_lpae = bitmap_bit_p (arm_active_target.isa, isa_bit_lpae);
   arm_arch_i8mm = bitmap_bit_p (arm_active_target.isa, isa_bit_i8mm);
   arm_arch_bf16 = bitmap_bit_p (arm_active_target.isa, isa_bit_bf16);
@@ -21139,6 +21148,9 @@ arm_compute_save_core_reg_mask (void)
 
   save_reg_mask |= arm_compute_save_reg0_reg12_mask ();
 
+  if (arm_current_function_pac_enabled_p ())
+    save_reg_mask |= 1 << IP_REGNUM;
+
   /* Decide if we need to save the link register.
      Interrupt routines have their own banked link register,
      so they never need to save it.
@@ -23362,6 +23374,12 @@ output_probe_stack_range (rtx reg1, rtx reg2)
   return "";
 }
 
+static bool
+aarch_bti_enabled ()
+{
+  return false;
+}
+
 /* Generate the prologue instructions for entry into an ARM or Thumb-2
    function.  */
 void
@@ -23440,12 +23458,13 @@ arm_expand_prologue (void)
 
   /* The static chain register is the same as the IP register.  If it is
      clobbered when creating the frame, we need to save and restore it.  */
-  clobber_ip = IS_NESTED (func_type)
-	       && ((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
-		   || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
-			|| flag_stack_clash_protection)
-		       && !df_regs_ever_live_p (LR_REGNUM)
-		       && arm_r3_live_at_start_p ()));
+  clobber_ip = (IS_NESTED (func_type)
+                && (((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
+                     || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
+                          || flag_stack_clash_protection)
+                         && !df_regs_ever_live_p (LR_REGNUM)
+                         && arm_r3_live_at_start_p ()))
+                    || (arm_current_function_pac_enabled_p ())));
 
   /* Find somewhere to store IP whilst the frame is being created.
      We try the following places in order:
@@ -23470,7 +23489,8 @@ arm_expand_prologue (void)
 	{
 	  rtx addr, dwarf;
 
-	  gcc_assert(arm_compute_static_chain_stack_bytes() == 4);
+	  gcc_assert(arm_compute_static_chain_stack_bytes() == 4
+                     || arm_current_function_pac_enabled_p ());
 	  saved_regs += 4;
 
 	  addr = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
@@ -23521,6 +23541,17 @@ arm_expand_prologue (void)
 	}
     }
 
+  if (arm_current_function_pac_enabled_p ())
+    {
+      /* If IP was clobbered we only emit a PAC instruction as the BTI
+         one will be added before the push of the clobbered IP (if
+         necessary) by the bti pass.  */
+      if (aarch_bti_enabled () && !clobber_ip)
+	emit_insn (gen_pacbti_nop ());
+      else
+	emit_insn (gen_pac_nop ());
+    }
+
   if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
     {
       if (IS_INTERRUPT (func_type))
@@ -27309,7 +27340,7 @@ thumb2_expand_return (bool simple_return)
 	 to assert it for now to ensure that future code changes do not silently
 	 change this behavior.  */
       gcc_assert (!IS_CMSE_ENTRY (arm_current_func_type ()));
-      if (num_regs == 1)
+      if (num_regs == 1 && !arm_current_function_pac_enabled_p ())
         {
           rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
           rtx reg = gen_rtx_REG (SImode, PC_REGNUM);
@@ -27324,10 +27355,20 @@ thumb2_expand_return (bool simple_return)
         }
       else
         {
-          saved_regs_mask &= ~ (1 << LR_REGNUM);
-          saved_regs_mask |=   (1 << PC_REGNUM);
-          arm_emit_multi_reg_pop (saved_regs_mask);
-        }
+	  if (arm_current_function_pac_enabled_p ())
+	    {
+	      gcc_assert (!(saved_regs_mask & (1 << PC_REGNUM)));
+	      arm_emit_multi_reg_pop (saved_regs_mask);
+	      emit_insn (gen_aut_nop ());
+	      emit_jump_insn (simple_return_rtx);
+	    }
+	  else
+	    {
+	      saved_regs_mask &= ~ (1 << LR_REGNUM);
+	      saved_regs_mask |=   (1 << PC_REGNUM);
+	      arm_emit_multi_reg_pop (saved_regs_mask);
+	    }
+	}
     }
   else
     {
@@ -27733,7 +27774,8 @@ arm_expand_epilogue (bool really_return)
           && really_return
           && crtl->args.pretend_args_size == 0
           && saved_regs_mask & (1 << LR_REGNUM)
-          && !crtl->calls_eh_return)
+          && !crtl->calls_eh_return
+	  && !arm_current_function_pac_enabled_p ())
         {
           saved_regs_mask &= ~(1 << LR_REGNUM);
           saved_regs_mask |= (1 << PC_REGNUM);
@@ -27847,6 +27889,9 @@ arm_expand_epilogue (bool really_return)
 	}
     }
 
+  if (arm_current_function_pac_enabled_p ())
+    emit_insn (gen_aut_nop ());
+
   if (!really_return)
     return;
 
@@ -32941,6 +32986,15 @@ arm_fusion_enabled_p (tune_params::fuse_ops op)
   return current_tune->fusible_ops & op;
 }
 
+/* Return TRUE if return address signing mechanism is enabled.  */
+bool
+arm_current_function_pac_enabled_p (void)
+{
+  return (aarch_ra_sign_scope == AARCH_FUNCTION_ALL
+          || (aarch_ra_sign_scope == AARCH_FUNCTION_NON_LEAF
+              && !crtl->is_leaf));
+}
+
 /* Implement TARGET_SCHED_CAN_SPECULATE_INSN.  Return true if INSN can be
    scheduled for speculative execution.  Reject the long-running division
    and square-root instructions.  */
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 3495ab857ea..e33425bbf42 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -510,6 +510,10 @@ extern int arm_arch8_3;
 /* Nonzero if this chip supports the ARM Architecture 8.4 extensions.  */
 extern int arm_arch8_4;
 
+/* Nonzero if this chip supports the ARM Architecture 8-M Mainline
+   extensions.  */
+extern int arm_arch8m_main;
+
 /* Nonzero if this chip supports the ARM Architecture 8.1-M Mainline
    extensions.  */
 extern int arm_arch8_1m_main;
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 60468f6182c..7255fa98f5d 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -12890,6 +12890,29 @@ (define_insn "*speculation_barrier_insn"
    (set_attr "length" "8")]
 )
 
+(define_insn "pac_nop"
+  [(set (reg:SI IP_REGNUM)
+	(unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
+		   UNSPEC_PAC_NOP))]
+  "arm_arch8m_main"
+  "pac\t%|ip, %|lr, %|sp"
+  [(set_attr "conds" "unconditional")])
+
+(define_insn "pacbti_nop"
+  [(set (reg:SI IP_REGNUM)
+	(unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
+		   UNSPEC_PACBTI_NOP))]
+  "arm_arch8m_main"
+  "pacbti\t%|ip, %|lr, %|sp"
+  [(set_attr "conds" "unconditional")])
+
+(define_insn "aut_nop"
+  [(unspec:SI [(reg:SI IP_REGNUM) (reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
+	      UNSPEC_AUT_NOP)]
+  "arm_arch8m_main"
+  "aut\t%|ip, %|lr, %|sp"
+  [(set_attr "conds" "unconditional")])
+
 ;; Vector bits common to IWMMXT, Neon and MVE
 (include "vec-common.md")
 ;; Load the Intel Wireless Multimedia Extension patterns
diff --git a/gcc/config/arm/unspecs.md b/gcc/config/arm/unspecs.md
index 7748e784379..dbe243a03f6 100644
--- a/gcc/config/arm/unspecs.md
+++ b/gcc/config/arm/unspecs.md
@@ -159,6 +159,9 @@ (define_c_enum "unspec" [
   UNSPEC_VCDE		; Custom Datapath Extension instruction.
   UNSPEC_VCDEA		; Custom Datapath Extension instruction.
   UNSPEC_DLS		; Used for DLS (Do Loop Start), Armv8.1-M Mainline instruction
+  UNSPEC_PAC_NOP	; Represents PAC signing LR
+  UNSPEC_PACBTI_NOP	; Represents PAC signing LR + valid landing pad
+  UNSPEC_AUT_NOP	; Represents PAC verifying LR
 ])
 
 
diff --git a/gcc/testsuite/gcc.target/arm/pac-1.c b/gcc/testsuite/gcc.target/arm/pac-1.c
new file mode 100644
index 00000000000..6cd64dbd014
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-1.c
@@ -0,0 +1,12 @@
+/* Testing return address signing.  */
+/* { dg-do run } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-require-effective-target arm_pacbti_hw } */
+/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-not "\tbti" } } */
+
diff --git a/gcc/testsuite/gcc.target/arm/pac-2.c b/gcc/testsuite/gcc.target/arm/pac-2.c
new file mode 100644
index 00000000000..945ce938592
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-2.c
@@ -0,0 +1,11 @@
+/* Testing return address signing.  */
+/* { dg-do run } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-require-effective-target arm_pacbti_hw } */
+/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret -mthumb -mfloat-abi=hard --save-temps -O0" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler "pac\tip, lr, sp" } } */
+/* { dg-final { scan-assembler "aut\tip, lr, sp" } } */
+/* { dg-final { scan-assembler-not "\tbti" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-3.c b/gcc/testsuite/gcc.target/arm/pac-3.c
new file mode 100644
index 00000000000..47e290a5840
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-3.c
@@ -0,0 +1,11 @@
+/* Testing return address signing.  */
+/* { dg-do run } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-require-effective-target arm_pacbti_hw } */
+/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=bti+pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O2" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler-times "pacbti\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-not "\tbti" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-4.c b/gcc/testsuite/gcc.target/arm/pac-4.c
new file mode 100644
index 00000000000..cf915cdba50
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-4.c
@@ -0,0 +1,10 @@
+/* Testing return address signing.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mthumb -mfloat-abi=hard --save-temps -O2" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler-not "\tbti\t" } } */
+/* { dg-final { scan-assembler-not "\tpac\t" } } */
+/* { dg-final { scan-assembler-not "\tpacbti\t" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-5.c b/gcc/testsuite/gcc.target/arm/pac-5.c
new file mode 100644
index 00000000000..c70087eb6b6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-5.c
@@ -0,0 +1,28 @@
+/* Testing return address signing.  */
+/* { dg-do run } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-require-effective-target arm_pacbti_hw } */
+/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
+
+#include <stdlib.h>
+
+int
+__attribute__((noinline))
+foo1 (int a, int b)
+{
+  int square (int z) { return z * z; }
+  return square (a) + square (b);
+}
+
+int
+main (void)
+{
+  if (foo1 (1, 2) != 5)
+    abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-not "\tbti" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-6.c b/gcc/testsuite/gcc.target/arm/pac-6.c
new file mode 100644
index 00000000000..c5329f0ef48
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-6.c
@@ -0,0 +1,18 @@
+/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-options "-march=armv8.1-m.main+fp -mbranch-protection=pac-ret+leaf -mthumb --save-temps -O0 -g" } */
+
+int i;
+
+void foo (int);
+
+int bar()
+{
+  foo (i);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "pac\tip, lr, sp" } } */
+/* { dg-final { scan-assembler "aut\tip, lr, sp" } } */
+/* { dg-final { scan-assembler-not "bti" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-7.c b/gcc/testsuite/gcc.target/arm/pac-7.c
new file mode 100644
index 00000000000..cdaebca5cfa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-7.c
@@ -0,0 +1,32 @@
+/* Testing return address signing.  */
+/* { dg-do run } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-require-effective-target arm_pacbti_hw } */
+/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
+
+#include <stdlib.h>
+
+int
+__attribute__((noinline))
+foo1 (int a, int b)
+{
+  int x = 4;
+  int foo2 (int a, int b)
+  {
+    return a + b + x;
+  }
+  return foo2 (a, b);
+}
+
+int
+main (void)
+{
+  if (foo1 (1, 2) != 7)
+    abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-not "\tbti" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac-8.c b/gcc/testsuite/gcc.target/arm/pac-8.c
new file mode 100644
index 00000000000..3f37dcfa5c8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac-8.c
@@ -0,0 +1,34 @@
+/* Testing return address signing.  */
+/* { dg-do run } */
+/* { dg-require-effective-target mbranch_protection_ok } */
+/* { dg-require-effective-target arm_pacbti_hw } */
+/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+int acc (int n, ...)
+{
+    int sum = 0;
+    va_list ptr;
+
+    va_start (ptr, n);
+
+    for (int i = 0; i < n; i++)
+        sum += va_arg (ptr, int);
+    va_end (ptr);
+
+    return sum;
+}
+
+int main()
+{
+  if (acc (3, 1, 2, 3) != 6)
+    abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-not "\tbti" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pac.h b/gcc/testsuite/gcc.target/arm/pac.h
new file mode 100644
index 00000000000..7355e6b2954
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pac.h
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+int
+__attribute__((noinline))
+foo1 (int a, int b)
+{
+  return a + b;
+}
+
+int
+main (void)
+{
+  if (foo1 (1, 2) != 3)
+    abort ();
+
+  return 0;
+}
