diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs
index 50a35fe3dcf1d..6b31f14410d2f 100644
--- a/src/librustc_codegen_llvm/context.rs
+++ b/src/librustc_codegen_llvm/context.rs
@@ -12,7 +12,7 @@ use rustc_codegen_ssa::traits::*;
 use crate::callee::get_fn;
 use rustc::bug;
 use rustc::mir::mono::CodegenUnit;
-use rustc::session::config::{self, DebugInfo};
+use rustc::session::config::{self, CFGuard, DebugInfo};
 use rustc::session::Session;
 use rustc::ty::layout::{
     FnAbiExt, HasParamEnv, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx,
@@ -227,6 +227,16 @@ pub unsafe fn create_module(
         llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1);
     }
 
+    // Set module flags to enable Windows Control Flow Guard (/guard:cf) metadata
+    // only (`cfguard=1`) or metadata and checks (`cfguard=2`).
+    match sess.opts.debugging_opts.control_flow_guard {
+        CFGuard::Disabled => {}
+        CFGuard::NoChecks => {
+            llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 1)
+        }
+        CFGuard::Checks => llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 2),
+    }
+
     llmod
 }
 
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs
index f56a4170c0a4b..bc0322ffe1d48 100644
--- a/src/librustc_codegen_ssa/back/link.rs
+++ b/src/librustc_codegen_ssa/back/link.rs
@@ -1,7 +1,7 @@
 use rustc::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind};
 use rustc::middle::dependency_format::Linkage;
 use rustc::session::config::{
-    self, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer,
+    self, CFGuard, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer,
 };
 use rustc::session::search_paths::PathKind;
 /// For all the linkers we support, and information they might
@@ -1294,6 +1294,10 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
         cmd.pgo_gen();
     }
 
+    if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled {
+        cmd.control_flow_guard();
+    }
+
     // FIXME (#2397): At some point we want to rpath our guesses as to
     // where extern libraries might live, based on the
     // addl_lib_search_paths
diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs
index 695f171dfb49c..5aafb8a12d74b 100644
--- a/src/librustc_codegen_ssa/back/linker.rs
+++ b/src/librustc_codegen_ssa/back/linker.rs
@@ -106,6 +106,7 @@ pub trait Linker {
     fn no_relro(&mut self);
     fn optimize(&mut self);
     fn pgo_gen(&mut self);
+    fn control_flow_guard(&mut self);
     fn debuginfo(&mut self);
     fn no_default_libraries(&mut self);
     fn build_dylib(&mut self, out_filename: &Path);
@@ -360,6 +361,10 @@ impl<'a> Linker for GccLinker<'a> {
         self.cmd.arg("__llvm_profile_runtime");
     }
 
+    fn control_flow_guard(&mut self) {
+        self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
+    }
+
     fn debuginfo(&mut self) {
         if let DebugInfo::None = self.sess.opts.debuginfo {
             // If we are building without debuginfo enabled and we were called with
@@ -660,6 +665,10 @@ impl<'a> Linker for MsvcLinker<'a> {
         // Nothing needed here.
     }
 
+    fn control_flow_guard(&mut self) {
+        self.cmd.arg("/guard:cf");
+    }
+
     fn debuginfo(&mut self) {
         // This will cause the Microsoft linker to generate a PDB file
         // from the CodeView line tables in the object files.
@@ -862,6 +871,10 @@ impl<'a> Linker for EmLinker<'a> {
         // noop, but maybe we need something like the gnu linker?
     }
 
+    fn control_flow_guard(&mut self) {
+        self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
+    }
+
     fn debuginfo(&mut self) {
         // Preserve names or generate source maps depending on debug info
         self.cmd.arg(match self.sess.opts.debuginfo {
@@ -1058,6 +1071,10 @@ impl<'a> Linker for WasmLd<'a> {
 
     fn debuginfo(&mut self) {}
 
+    fn control_flow_guard(&mut self) {
+        self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
+    }
+
     fn no_default_libraries(&mut self) {}
 
     fn build_dylib(&mut self, _out_filename: &Path) {
@@ -1233,6 +1250,10 @@ impl<'a> Linker for PtxLinker<'a> {
 
     fn no_default_libraries(&mut self) {}
 
+    fn control_flow_guard(&mut self) {
+        self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
+    }
+
     fn build_dylib(&mut self, _out_filename: &Path) {}
 
     fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {}
diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs
index aa492b566e59e..813d14d616d42 100644
--- a/src/librustc_session/config.rs
+++ b/src/librustc_session/config.rs
@@ -70,6 +70,19 @@ impl FromStr for Sanitizer {
     }
 }
 
+/// The different settings that the `-Z control_flow_guard` flag can have.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum CFGuard {
+    /// Do not emit Control Flow Guard metadata or checks.
+    Disabled,
+
+    /// Emit Control Flow Guard metadata but no checks.
+    NoChecks,
+
+    /// Emit Control Flow Guard metadata and checks.
+    Checks,
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Hash)]
 pub enum OptLevel {
     No,         // -O0
@@ -1980,8 +1993,8 @@ impl PpMode {
 /// how the hash should be calculated when adding a new command-line argument.
 crate mod dep_tracking {
     use super::{
-        CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, OutputTypes,
-        Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion,
+        CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel,
+        OutputTypes, Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion,
     };
     use crate::lint;
     use crate::utils::NativeLibraryKind;
@@ -2053,6 +2066,7 @@ crate mod dep_tracking {
     impl_dep_tracking_hash_via_hash!(NativeLibraryKind);
     impl_dep_tracking_hash_via_hash!(Sanitizer);
     impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
+    impl_dep_tracking_hash_via_hash!(CFGuard);
     impl_dep_tracking_hash_via_hash!(TargetTriple);
     impl_dep_tracking_hash_via_hash!(Edition);
     impl_dep_tracking_hash_via_hash!(LinkerPluginLto);
diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs
index 34da2188a51d2..f7f8ec8604561 100644
--- a/src/librustc_session/options.rs
+++ b/src/librustc_session/options.rs
@@ -263,6 +263,8 @@ macro_rules! options {
         pub const parse_sanitizer_list: Option<&str> =
             Some("comma separated list of sanitizers");
         pub const parse_sanitizer_memory_track_origins: Option<&str> = None;
+        pub const parse_cfguard: Option<&str> =
+            Some("either `disabled`, `nochecks`, or `checks`");
         pub const parse_linker_flavor: Option<&str> =
             Some(::rustc_target::spec::LinkerFlavor::one_of());
         pub const parse_optimization_fuel: Option<&str> =
@@ -288,7 +290,7 @@ macro_rules! options {
     #[allow(dead_code)]
     mod $mod_set {
         use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath,
-            SymbolManglingVersion};
+            SymbolManglingVersion, CFGuard};
         use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel};
         use std::path::PathBuf;
         use std::str::FromStr;
@@ -499,6 +501,16 @@ macro_rules! options {
             }
         }
 
+        fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool {
+            match v {
+                Some("disabled") => *slot = CFGuard::Disabled,
+                Some("nochecks") => *slot = CFGuard::NoChecks,
+                Some("checks") => *slot = CFGuard::Checks,
+                _ => return false,
+            }
+            true
+        }
+
         fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
             match v.and_then(LinkerFlavor::from_str) {
                 Some(lf) => *slote = Some(lf),
@@ -950,6 +962,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
          (such as entering an empty infinite loop) by inserting llvm.sideeffect"),
     deduplicate_diagnostics: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
         "deduplicate identical diagnostics"),
+    control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [UNTRACKED],
+        "use Windows Control Flow Guard (`disabled`, `nochecks` or `checks`)"),
     no_link: bool = (false, parse_bool, [TRACKED],
         "compile without linking"),
 }
diff --git a/src/test/codegen/cfguard_checks.rs b/src/test/codegen/cfguard_checks.rs
new file mode 100644
index 0000000000000..40a7353eac045
--- /dev/null
+++ b/src/test/codegen/cfguard_checks.rs
@@ -0,0 +1,10 @@
+// compile-flags: -Z control_flow_guard=checks
+
+#![crate_type = "lib"]
+
+// A basic test function.
+pub fn test() {
+}
+
+// Ensure the module flag cfguard=2 is present
+// CHECK: !"cfguard", i32 2
diff --git a/src/test/codegen/cfguard_disabled.rs b/src/test/codegen/cfguard_disabled.rs
new file mode 100644
index 0000000000000..d1747931e15c8
--- /dev/null
+++ b/src/test/codegen/cfguard_disabled.rs
@@ -0,0 +1,10 @@
+// compile-flags: -Z control_flow_guard=disabled
+
+#![crate_type = "lib"]
+
+// A basic test function.
+pub fn test() {
+}
+
+// Ensure the module flag cfguard is not present
+// CHECK-NOT: !"cfguard"
diff --git a/src/test/codegen/cfguard_nochecks.rs b/src/test/codegen/cfguard_nochecks.rs
new file mode 100644
index 0000000000000..c5d7afbae257b
--- /dev/null
+++ b/src/test/codegen/cfguard_nochecks.rs
@@ -0,0 +1,10 @@
+// compile-flags: -Z control_flow_guard=nochecks
+
+#![crate_type = "lib"]
+
+// A basic test function.
+pub fn test() {
+}
+
+// Ensure the module flag cfguard=1 is present
+// CHECK: !"cfguard", i32 1