diff --git a/gcc/config.gcc b/gcc/config.gcc
index 2021bdf9d2f..004e1dfa8d8 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -353,7 +353,7 @@ arc*-*-*)
 	;;
 arm*-*-*)
 	cpu_type=arm
-	extra_objs="arm-builtins.o arm-mve-builtins.o aarch-common.o"
+	extra_objs="arm-builtins.o arm-mve-builtins.o aarch-common.o aarch-bti-insert.o"
 	extra_headers="mmintrin.h arm_neon.h arm_acle.h arm_fp16.h arm_cmse.h arm_bf16.h arm_mve_types.h arm_mve.h arm_cde.h"
 	target_type_format_char='%'
 	c_target_objs="arm-c.o"
diff --git a/gcc/config/arm/arm-passes.def b/gcc/config/arm/arm-passes.def
new file mode 100644
index 00000000000..71d6b563640
--- /dev/null
+++ b/gcc/config/arm/arm-passes.def
@@ -0,0 +1,21 @@
+/* Arm-specific passes declarations.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   Contributed by Arm Ltd.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_bti);
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 84764bf27ce..6befb6c4445 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -24,6 +24,8 @@
 
 #include "sbitmap.h"
 
+rtl_opt_pass *make_pass_insert_bti (gcc::context *ctxt);
+
 extern enum unwind_info_type arm_except_unwind_info (struct gcc_options *);
 extern int use_return_insn (int, rtx);
 extern bool use_simple_return_p (void);
diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index fa0f9a61498..26d4c1502f2 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -23374,12 +23374,6 @@ 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
@@ -32992,6 +32986,61 @@ arm_current_function_pac_enabled_p (void)
               && !crtl->is_leaf));
 }
 
+/* Return TRUE if Branch Target Identification Mechanism is enabled.  */
+bool
+aarch_bti_enabled (void)
+{
+  return aarch_enable_bti == 1;
+}
+
+/* Check if INSN is a BTI J insn.  */
+bool
+aarch_bti_j_insn_p (rtx_insn *insn)
+{
+  if (!insn || !INSN_P (insn))
+    return false;
+
+  rtx pat = PATTERN (insn);
+  return GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPEC_BTI_NOP;
+}
+
+/* Check if X (or any sub-rtx of X) is a PACIASP/PACIBSP instruction.  */
+bool
+aarch_pac_insn_p (rtx x)
+{
+  if (!x || !INSN_P (x))
+    return false;
+
+  rtx pat = PATTERN (x);
+
+  if (GET_CODE (pat) == SET)
+    {
+      rtx tmp = XEXP (pat, 1);
+      if (tmp
+	  && GET_CODE (tmp) == UNSPEC
+	  && (XINT (tmp, 1) == UNSPEC_PAC_NOP
+	      || XINT (tmp, 1) == UNSPEC_PACBTI_NOP))
+	return true;
+    }
+
+  return false;
+}
+
+/* The following two functions are for code compatibility with aarch64
+   code, this even if in arm we have only one bti instruction.  */
+
+rtx
+aarch_gen_bti_c (void)
+{
+  return gen_bti_nop ();
+}
+
+rtx
+aarch_gen_bti_j (void)
+{
+  return gen_bti_nop ();
+}
+
 /* 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.md b/gcc/config/arm/arm.md
index 7255fa98f5d..6e86811ee05 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -12913,6 +12913,13 @@ (define_insn "aut_nop"
   "aut\t%|ip, %|lr, %|sp"
   [(set_attr "conds" "unconditional")])
 
+(define_insn "bti_nop"
+  [(unspec_volatile [(const_int 0)] UNSPEC_BTI_NOP)]
+  "arm_arch8m_main"
+  "bti"
+  [(set_attr "conds" "unconditional")
+   (set_attr "type" "nop")])
+
 ;; 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/t-arm b/gcc/config/arm/t-arm
index 041cc6ec045..683342cb528 100644
--- a/gcc/config/arm/t-arm
+++ b/gcc/config/arm/t-arm
@@ -175,3 +175,13 @@ arm-d.o: $(srcdir)/config/arm/arm-d.cc
 arm-common.o: arm-cpu-cdata.h
 
 driver-arm.o: arm-native.h
+
+PASSES_EXTRA += $(srcdir)/config/arm/arm-passes.def
+
+aarch-bti-insert.o: $(srcdir)/config/arm/aarch-bti-insert.cc \
+    $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(REGS_H) insn-config.h $(RTL_BASE_H) \
+    dominance.h cfg.h cfganal.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(RECOG_H) \
+    output.h hash-map.h $(DF_H) $(OBSTACK_H) $(TARGET_H) $(RTL_H) \
+    $(CONTEXT_H) $(TREE_PASS_H) regrename.h
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+		$(srcdir)/config/arm/aarch-bti-insert.cc
diff --git a/gcc/config/arm/unspecs.md b/gcc/config/arm/unspecs.md
index dbe243a03f6..78e723a4b3c 100644
--- a/gcc/config/arm/unspecs.md
+++ b/gcc/config/arm/unspecs.md
@@ -162,6 +162,7 @@ (define_c_enum "unspec" [
   UNSPEC_PAC_NOP	; Represents PAC signing LR
   UNSPEC_PACBTI_NOP	; Represents PAC signing LR + valid landing pad
   UNSPEC_AUT_NOP	; Represents PAC verifying LR
+  UNSPEC_BTI_NOP	; Represent BTI
 ])
 
 
diff --git a/gcc/testsuite/gcc.target/arm/bti-1.c b/gcc/testsuite/gcc.target/arm/bti-1.c
new file mode 100644
index 00000000000..79dd8010d2d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/bti-1.c
@@ -0,0 +1,12 @@
+/* Check that GCC does bti instruction.  */
+/* { dg-do compile } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
+/* { dg-options "-march=armv8.1-m.main -mthumb -mfloat-abi=softfp -mbranch-protection=bti --save-temps" } */
+
+int
+main (void)
+{
+  return 0;
+}
+
+/* { dg-final { scan-assembler "bti" } } */
diff --git a/gcc/testsuite/gcc.target/arm/bti-2.c b/gcc/testsuite/gcc.target/arm/bti-2.c
new file mode 100644
index 00000000000..33910563849
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/bti-2.c
@@ -0,0 +1,58 @@
+/* { dg-do compile } */
+/* -Os to create jump table.  */
+/* { dg-options "-Os" } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
+/* { dg-options "-march=armv8.1-m.main -mthumb -mfloat-abi=softfp -mbranch-protection=bti --save-temps" } */
+
+extern int f1 (void);
+extern int f2 (void);
+extern int f3 (void);
+extern int f4 (void);
+extern int f5 (void);
+extern int f6 (void);
+extern int f7 (void);
+extern int f8 (void);
+extern int f9 (void);
+extern int f10 (void);
+
+int (*ptr) (void);
+
+int
+f_jump_table (int y, int n)
+{
+  int i;
+  for (i = 0; i < n ;i ++)
+  {
+    switch (y)
+      {
+      case 0 : ptr = f1; break;
+      case 1 : ptr = f2; break;
+      case 2 : ptr = f3; break;
+      case 3 : ptr = f4; break;
+      case 4 : ptr = f5; break;
+      case 5 : ptr = f6; break;
+      case 6 : ptr = f7; break;
+      case 7 : ptr = f8; break;
+      case 8 : ptr = f9; break;
+      case 9 : ptr = f10; break;
+      default: break;
+      }
+    y += ptr ();
+  }
+  return (y == 0)? y+1:4;
+}
+
+int
+f_label_address ()
+{
+  static void * addr = &&lab1;
+  goto *addr;
+lab1:
+  addr = &&lab2;
+  return 1;
+lab2:
+  addr = &&lab1;
+  return 2;
+}
+
+/* { dg-final { scan-assembler-times "bti" 15 } } */
