diff --git a/gcc/common.opt b/gcc/common.opt
index a28ca13385a..ac12da52733 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -229,6 +229,10 @@ bool dump_base_name_prefixed = false
 Variable
 unsigned int flag_zero_call_used_regs
 
+;; What the CF check should instrument
+Variable
+unsigned int flag_cf_protection = 0
+
 ###
 Driver
 
@@ -1886,28 +1890,10 @@ fcf-protection
 Common RejectNegative Alias(fcf-protection=,full)
 
 fcf-protection=
-Common Joined RejectNegative Enum(cf_protection_level) Var(flag_cf_protection) Init(CF_NONE)
+Common Joined
 -fcf-protection=[full|branch|return|none|check]	Instrument functions with checks to verify jump/call/return control-flow transfer
 instructions have valid targets.
 
-Enum
-Name(cf_protection_level) Type(enum cf_protection_level) UnknownError(unknown Control-Flow Protection Level %qs)
-
-EnumValue
-Enum(cf_protection_level) String(full) Value(CF_FULL)
-
-EnumValue
-Enum(cf_protection_level) String(branch) Value(CF_BRANCH)
-
-EnumValue
-Enum(cf_protection_level) String(return) Value(CF_RETURN)
-
-EnumValue
-Enum(cf_protection_level) String(check) Value(CF_CHECK)
-
-EnumValue
-Enum(cf_protection_level) String(none) Value(CF_NONE)
-
 finstrument-functions
 Common Var(flag_instrument_function_entry_exit,1)
 Instrument function entry and exit with profiling calls.
diff --git a/gcc/lto-wrapper.cc b/gcc/lto-wrapper.cc
index 5186d040ce0..568c8af659d 100644
--- a/gcc/lto-wrapper.cc
+++ b/gcc/lto-wrapper.cc
@@ -359,26 +359,33 @@ merge_and_complain (vec<cl_decoded_option> &decoded_options,
 	case OPT_fcf_protection_:
 	  /* Default to link-time option, else append or check identical.  */
 	  if (!cf_protection_option
-	      || cf_protection_option->value == CF_CHECK)
+	      || !memcmp (cf_protection_option->arg, "check", 5))
 	    {
+	      const char* parg = decoded_options[existing_opt].arg;
 	      if (existing_opt == -1)
 		decoded_options.safe_push (*foption);
-	      else if (decoded_options[existing_opt].value != foption->value)
+	      else if ((strlen (parg) != strlen (foption->arg))
+		       || memcmp (parg, foption->arg, strlen (foption->arg)))
 		{
 		  if (cf_protection_option
-		      && cf_protection_option->value == CF_CHECK)
+		      && !memcmp (cf_protection_option->arg, "check", 5))
 		    fatal_error (input_location,
 				 "option %qs with mismatching values"
 				 " (%s, %s)",
 				 "-fcf-protection",
-				 decoded_options[existing_opt].arg,
+				 parg,
 				 foption->arg);
 		  else
 		    {
 		      /* Merge and update the -fcf-protection option.  */
-		      decoded_options[existing_opt].value
-			&= (foption->value & CF_FULL);
-		      switch (decoded_options[existing_opt].value)
+		      HOST_WIDE_INT flags1
+			= parse_cf_protection_options (foption->arg,
+						       input_location);
+		      HOST_WIDE_INT flags2
+			= parse_cf_protection_options (parg,
+						       input_location);
+		      flags2 &= (flags1 & CF_FULL);
+		      switch (flags2)
 			{
 			case CF_NONE:
 			  decoded_options[existing_opt].arg = "none";
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 86b94d62b58..6389383bc92 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -2187,6 +2187,81 @@ get_closest_sanitizer_option (const string_fragment &arg,
   return bm.get_best_meaningful_candidate ();
 }
 
+unsigned int
+parse_cf_protection_options (const char *p, location_t loc)
+{
+  unsigned int flags = 0;
+  bool combined = false;
+  while (*p != 0)
+    {
+      size_t len;
+      const char *comma = strchr (p, ',');
+      if (comma == NULL)
+	len = strlen (p);
+      else
+	{
+	  combined = true;
+	  len = comma - p;
+	}
+
+      if (len == 0)
+	{
+	  p = comma + 1;
+	  continue;
+	}
+
+      switch (len)
+	{
+	case 4:
+	  if (!memcmp (p, "full", 4))
+	    {
+	      if (combined && flags != CF_NONE)
+		warning_at (loc, 0, "better to use %<-fcf-protection=%> "
+			    "option: full alone instead of in a combination");
+	      flags |= CF_FULL;
+	    }
+	  else if (!memcmp (p, "none", 4))
+	    {
+	      if (combined && flags != CF_NONE)
+		warning_at (loc, 0, "combination of %<-fcf-protection=%> "
+			    "option: none is ignored");
+	    }
+	  else
+	    error_at (loc, "unrecognized argument to %<-fcf-protection=%> "
+		      "option: %.4s", p);
+	  break;
+	case 5:
+	  if (!memcmp (p, "check", 5))
+	    {
+	      if (combined && flags != CF_CHECK)
+		error_at (loc, "Combination for %<-fcf-protection=%> "
+			  "option: check is not valid");
+	      flags |= CF_CHECK;
+	    }
+	  else
+	    error_at (loc, "unrecognized argument to %<-fcf-protection=%> "
+		      "option: %.5s", p);
+	  break;
+	case 6:
+	  if (!memcmp (p, "branch", 6))
+	    flags |= CF_BRANCH;
+	  else if (!memcmp (p, "return", 6))
+	    flags |= CF_RETURN;
+	  else
+	    error_at (loc, "unrecognized argument to %<-fcf-protection=%> "
+		      "option: %.6s", p);
+	  break;
+	default:
+	  error_at (loc, "unrecognized argument to %<-fcf-protection=%> "
+		    "option: %.*s", (int) len, p);
+	}
+
+      if (comma == NULL)
+	break;
+      p = comma + 1;
+    }
+  return flags;
+}
 /* Parse comma separated sanitizer suboptions from P for option SCODE,
    adjust previous FLAGS and return new ones.  If COMPLAIN is false,
    don't issue diagnostics.  */
@@ -2671,6 +2746,10 @@ common_handle_option (struct gcc_options *opts,
     case OPT__completion_:
       break;
 
+    case OPT_fcf_protection_:
+      opts->x_flag_cf_protection
+	= parse_cf_protection_options (arg, loc);
+      break;
     case OPT_fsanitize_:
       opts_set->x_flag_sanitize = true;
       opts->x_flag_sanitize
diff --git a/gcc/opts.h b/gcc/opts.h
index 9959a440ca1..00d396d95f8 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -425,6 +425,7 @@ extern void control_warning_option (unsigned int opt_index, int kind,
 extern char *write_langs (unsigned int mask);
 extern void print_ignored_options (void);
 extern void handle_common_deferred_options (void);
+extern unsigned int parse_cf_protection_options (const char*, location_t);
 unsigned int parse_sanitizer_options (const char *, location_t, int,
 				      unsigned int, int, bool);
 
diff --git a/gcc/testsuite/c-c++-common/fcf-protection-10.c b/gcc/testsuite/c-c++-common/fcf-protection-10.c
new file mode 100644
index 00000000000..8692a08374b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fcf-protection-10.c
@@ -0,0 +1,3 @@
+/* { dg-do compile { target { "i?86-*-* x86_64-*-*" } } } */
+/* { dg-options "-fcf-protection=branch,check" } */
+/* { dg-error "Combination for '-fcf-protection=' option: check is not valid" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/fcf-protection-11.c b/gcc/testsuite/c-c++-common/fcf-protection-11.c
new file mode 100644
index 00000000000..2e566350ccd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fcf-protection-11.c
@@ -0,0 +1,2 @@
+/* { dg-do compile { target { "i?86-*-* x86_64-*-*" } } } */
+/* { dg-options "-fcf-protection=branch,return" } */
diff --git a/gcc/testsuite/c-c++-common/fcf-protection-12.c b/gcc/testsuite/c-c++-common/fcf-protection-12.c
new file mode 100644
index 00000000000..b39c2f8e25d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fcf-protection-12.c
@@ -0,0 +1,2 @@
+/* { dg-do compile { target { "i?86-*-* x86_64-*-*" } } } */
+/* { dg-options "-fcf-protection=return,branch" } */
diff --git a/gcc/testsuite/c-c++-common/fcf-protection-8.c b/gcc/testsuite/c-c++-common/fcf-protection-8.c
new file mode 100644
index 00000000000..33e46223b6b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fcf-protection-8.c
@@ -0,0 +1,3 @@
+/* { dg-do compile { target { "i?86-*-* x86_64-*-*" } } } */
+/* { dg-options "-fcf-protection=branch,none" } */
+/* { dg-warning "combination of '-fcf-protection=' option: none is ignored" "" { target { *-*-* } } 0 } */
diff --git a/gcc/testsuite/c-c++-common/fcf-protection-9.c b/gcc/testsuite/c-c++-common/fcf-protection-9.c
new file mode 100644
index 00000000000..7848fe4b959
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fcf-protection-9.c
@@ -0,0 +1,3 @@
+/* { dg-do compile { target { "i?86-*-* x86_64-*-*" } } } */
+/* { dg-options "-fcf-protection=branch,full" } */
+/* { dg-warning "better to use '-fcf-protection=' option: full alone instead of in a combination" "" { target { *-*-* } } 0 } */
diff --git a/gcc/testsuite/gcc.target/i386/pr89701-1.c b/gcc/testsuite/gcc.target/i386/pr89701-1.c
new file mode 100644
index 00000000000..1879c9ab4d8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr89701-1.c
@@ -0,0 +1,4 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-fcf-protection=branch,return" } */
+/* { dg-final { scan-assembler-times ".note.gnu.property" 1 } } */
+/* { dg-final { scan-assembler-times ".long	0x3" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr89701-2.c b/gcc/testsuite/gcc.target/i386/pr89701-2.c
new file mode 100644
index 00000000000..d5100575028
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr89701-2.c
@@ -0,0 +1,4 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-fcf-protection=return,branch" } */
+/* { dg-final { scan-assembler-times ".note.gnu.property" 1 } } */
+/* { dg-final { scan-assembler-times ".long	0x3" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr89701-3.c b/gcc/testsuite/gcc.target/i386/pr89701-3.c
new file mode 100644
index 00000000000..1505051a2bb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr89701-3.c
@@ -0,0 +1,5 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-fcf-protection=return,none" } */
+/* { dg-warning "combination of '-fcf-protection=' option: none is ignored" "" { target { *-*-linux* } } 0 } */
+/* { dg-final { scan-assembler-times ".note.gnu.property" 1 } } */
+/* { dg-final { scan-assembler-times ".long	0x2" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr89701-4.c b/gcc/testsuite/gcc.target/i386/pr89701-4.c
new file mode 100644
index 00000000000..242b8810abb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr89701-4.c
@@ -0,0 +1,5 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-fcf-protection=branch,none" } */
+/* { dg-warning "combination of '-fcf-protection=' option: none is ignored" "" { target { *-*-* } } 0 } */
+/* { dg-final { scan-assembler-times ".note.gnu.property" 1 } } */
+/* { dg-final { scan-assembler-times ".long	0x1" 1 } } */
