diff --git a/gcc/common/config/arm/arm-common.c b/gcc/common/config/arm/arm-common.c
index de898a74165db4d7250aa0097dfab682beb0f99c..188feebb15b52f389d5d0b3ec322be3017efd5a0 100644
--- a/gcc/common/config/arm/arm-common.c
+++ b/gcc/common/config/arm/arm-common.c
@@ -475,6 +475,156 @@ arm_parse_arch_option_name (const arch_option *list, const char *optname,
   return NULL;
 }
 
+static void
+arm_print_hint_for_pacbti_option ()
+{
+  const char *s = "pac-ret[+leaf][+b-key][+bti]"
+		  " | bti[+pac-ret[+leaf][+b-key]]";
+  inform (input_location, "valid arguments are: %s", s);
+}
+
+/* Progress *E to end of next token delimited by DELIMITER.
+   Cache old *E in *OE.  */
+static void
+arm_progress_next_token (const char **oe, const char **e,
+			 size_t *l, const char delimiter)
+{
+  *oe = *e + 1;
+  *e = strchr (*oe, delimiter);
+  *l = *e ? *e - *oe : strlen (*oe);
+}
+
+/* Parse options to -mbranch-protection.  */
+static const char*
+arm_parse_pac_ret_clause (const char *pacret, const char *optname,
+			  unsigned int *pacbti)
+{
+  const char *old_end = NULL;
+  const char *end = strchr (pacret, '+');
+  size_t len = end ? end - pacret : strlen (pacret);
+  if (len == 7 && strncmp (pacret, "pac-ret", len) == 0)
+    {
+      *pacbti |= 2;
+      if (end != NULL)
+	{
+	  /* pac-ret+...  */
+	  arm_progress_next_token (&old_end, &end, &len, '+');
+	  if (len == 4 && strncmp (old_end, "leaf", len) == 0)
+	    {
+	      *pacbti |= 8;
+	      if (end != NULL)
+		{
+		  /* pac-ret+leaf+...  */
+		  arm_progress_next_token (&old_end, &end, &len, '+');
+		  if (len == 5 && strncmp (old_end, "b-key", len) == 0)
+		    {
+		      /* Clear bit for A-key.  */
+		      *pacbti &= 0xfffffffd;
+		      *pacbti |= 4;
+		      /* A non-NULL end indicates its pointing to a '+'.
+			 Advance it to point to the next option in the string.  */
+		      if (end != NULL)
+			end++;
+		    }
+		  else
+		    /* This could be 'bti', leave it to caller to parse.  */
+		    end = old_end;
+		}
+	    }
+	  else if (len == 5 && strncmp (old_end, "b-key", len) == 0)
+	    {
+	      /* Clear bit for A-key.  */
+	      *pacbti &= 0xfffffffd;
+	      *pacbti |= 4;
+	      if (end != NULL)
+		{
+		  /* pac-ret+b-key+...  */
+		  arm_progress_next_token (&old_end, &end, &len, '+');
+		  if (len == 4 && strncmp (old_end, "leaf", len) == 0)
+		    {
+		      *pacbti |= 8;
+		      /* A non-NULL end indicates its pointing to a '+'.
+			 Advance it to point to the next option in the string.  */
+		      if (end != NULL)
+			end++;
+		    }
+		  else
+		    /* This could be 'bti', leave it to caller to parse.  */
+		    end = old_end;
+		}
+	    }
+	  else
+	    {
+	      /* This could be a 'bti' option, so leave it to the caller to
+		 parse.  Fall through to the return.  */
+	      end = old_end;
+	    }
+	}
+    }
+  else
+    {
+      error_at (input_location, "unrecognized %s argument: %s", optname, pacret);
+      arm_print_hint_for_pacbti_option ();
+      return NULL;
+    }
+
+  return end;
+}
+
+unsigned int
+arm_parse_pacbti_option (const char *pacbti, const char *optname, bool complain)
+{
+  unsigned int enable_pacbti = 0;
+  const char *end = strchr (pacbti, '+');
+  size_t len = end ? end - pacbti : strlen (pacbti);
+
+  if (strcmp (pacbti, "none") == 0)
+    return 0;
+
+  if (strcmp (pacbti, "standard") == 0)
+    return 0x3;
+
+  if (len == 3 && strncmp (pacbti, "bti", len) == 0)
+    {
+      /* bti+...  */
+      enable_pacbti |= 1;
+
+      if (end != NULL
+	  && arm_parse_pac_ret_clause (end + 1, optname, &enable_pacbti) != NULL
+	  && complain == true)
+	{
+	/* If the value returned in non-NULL, there's garbage at the end,
+	   so error.  */
+	  error_at (input_location, "unrecognized %s argument: %s",
+		    optname, pacbti);
+	  arm_print_hint_for_pacbti_option ();
+	}
+    }
+  else
+    {
+      /* <pac-ret-clause>+...  */
+      end = arm_parse_pac_ret_clause (pacbti, optname, &enable_pacbti);
+      if (end != NULL)
+	{
+	  if (strcmp (end, "bti") != 0)
+	    {
+	      if (complain == true)
+		{
+		  error_at (input_location, "unrecognized %s argument: %s",
+			    optname, pacbti);
+		  arm_print_hint_for_pacbti_option ();
+		}
+	    }
+	  else
+	    {
+	      enable_pacbti |= 1;
+	    }
+	}
+    }
+
+  return enable_pacbti;
+}
+
 /* List the permitted architecture option names.  If TARGET is a near
    miss for an entry, print out the suggested alternative.  */
 static void
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 9b1f61394ad7d778a6c0b84bc6c805f14709f598..1f6984199382848e6d105ba7afd571a49d1ee885 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -584,6 +584,10 @@ const arch_option *arm_parse_arch_option_name (const arch_option *,
 void arm_parse_option_features (sbitmap, const cpu_arch_option *,
 				const char *);
 
+
+unsigned int arm_parse_pacbti_option (const char *pacbti, const char *optname,
+				      bool complain = true);
+
 void arm_initialize_isa (sbitmap, const enum isa_feature *);
 
 const char * arm_gen_far_branch (rtx *, int, const char * , const char *);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 6c6e77fab666f4aeff023b1f949e3ca0a3545658..1f939a6b79a90430abf120e0aa075dfc1fab29a8 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -3217,6 +3217,13 @@ arm_configure_build_target (struct arm_build_target *target,
       tune_opts = strchr (opts->x_arm_tune_string, '+');
     }
 
+  if (opts->x_arm_branch_protection_string)
+    {
+      arm_enable_pacbti
+	= arm_parse_pacbti_option (opts->x_arm_branch_protection_string,
+				   "-mbranch-protection");
+    }
+
   if (arm_selected_arch)
     {
       arm_initialize_isa (target->isa, arm_selected_arch->common.isa_bits);
diff --git a/gcc/config/arm/arm.opt b/gcc/config/arm/arm.opt
index a7677eeb45c805d6a314f4d1d2d6063d8c0b687c..82dd665d327d514d9ad842905ddd3f9bb61e99a4 100644
--- a/gcc/config/arm/arm.opt
+++ b/gcc/config/arm/arm.opt
@@ -304,6 +304,13 @@ mbranch-cost=
 Target RejectNegative Joined UInteger Var(arm_branch_cost) Init(-1)
 Cost to assume for a branch insn.
 
+mbranch-protection=
+Target RejectNegative Joined Var(arm_branch_protection_string) Save
+Use branch-protection features.
+
+TargetVariable
+unsigned int arm_enable_pacbti = 0x0
+
 mgeneral-regs-only
 Target RejectNegative Mask(GENERAL_REGS_ONLY) Save
 Generate code which uses the core registers only (r0-r14).
