diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 5e78d6fc851..77aaa3010f2 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -479,6 +479,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             load: &'ll Value,
             scalar: abi::Scalar,
         ) {
+            if !scalar.is_always_valid(bx) {
+                bx.noundef_metadata(load);
+            }
+
             match scalar.value {
                 abi::Int(..) => {
                     if !scalar.is_always_valid(bx) {
@@ -1215,6 +1219,16 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
         }
     }
 
+    fn noundef_metadata(&mut self, load: &'ll Value) {
+        unsafe {
+            llvm::LLVMSetMetadata(
+                load,
+                llvm::MD_noundef as c_uint,
+                llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0),
+            );
+        }
+    }
+
     pub fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
         unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) }
     }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 31d1460e178..81ae97a80cc 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -442,6 +442,7 @@ pub enum MetadataType {
     MD_mem_parallel_loop_access = 10,
     MD_nonnull = 11,
     MD_type = 19,
+    MD_noundef = 29,
 }
 
 /// LLVMRustAsmDialect
diff --git a/src/test/codegen/loads.rs b/src/test/codegen/loads.rs
index 3c9ecec2cbf..977175a4a62 100644
--- a/src/test/codegen/loads.rs
+++ b/src/test/codegen/loads.rs
@@ -2,11 +2,87 @@
 
 #![crate_type = "lib"]
 
+use std::mem::MaybeUninit;
+use std::num::NonZeroU16;
+
 pub struct Bytes {
-  a: u8,
-  b: u8,
-  c: u8,
-  d: u8,
+    a: u8,
+    b: u8,
+    c: u8,
+    d: u8,
+}
+
+#[derive(Copy, Clone)]
+pub enum MyBool {
+    True,
+    False,
+}
+
+// CHECK-LABEL: @load_ref
+#[no_mangle]
+pub fn load_ref<'a>(x: &&'a i32) -> &'a i32 {
+// Alignment of a reference itself is target dependent, so just match any alignment:
+// the main thing we care about here is !nonnull and !noundef.
+// CHECK: load i32*, i32** %x, align {{[0-9]+}}, !nonnull !{{[0-9]+}}, !noundef !{{[0-9]+}}
+    *x
+}
+
+// CHECK-LABEL: @load_box
+#[no_mangle]
+pub fn load_box<'a>(x: Box<Box<i32>>) -> Box<i32> {
+// Alignment of a box itself is target dependent, so just match any alignment:
+// the main thing we care about here is !nonnull and !noundef.
+// CHECK: load i32*, i32** %x, align {{[0-9]+}}, !nonnull !{{[0-9]+}}, !noundef !{{[0-9]+}}
+    *x
+}
+
+// CHECK-LABEL: @load_bool
+#[no_mangle]
+pub fn load_bool(x: &bool) -> bool {
+// CHECK: load i8, i8* %x, align 1, !range ![[BOOL_RANGE:[0-9]+]], !noundef !{{[0-9]+}}
+    *x
+}
+
+// CHECK-LABEL: @load_maybeuninit_bool
+#[no_mangle]
+pub fn load_maybeuninit_bool(x: &MaybeUninit<bool>) -> MaybeUninit<bool> {
+// CHECK: load i8, i8* %x, align 1{{$}}
+    *x
+}
+
+// CHECK-LABEL: @load_enum_bool
+#[no_mangle]
+pub fn load_enum_bool(x: &MyBool) -> MyBool {
+// CHECK: load i8, i8* %x, align 1, !range ![[BOOL_RANGE]], !noundef !{{[0-9]+}}
+    *x
+}
+
+// CHECK-LABEL: @load_maybeuninit_enum_bool
+#[no_mangle]
+pub fn load_maybeuninit_enum_bool(x: &MaybeUninit<MyBool>) -> MaybeUninit<MyBool> {
+// CHECK: load i8, i8* %x, align 1{{$}}
+    *x
+}
+
+// CHECK-LABEL: @load_int
+#[no_mangle]
+pub fn load_int(x: &u16) -> u16 {
+// CHECK: load i16, i16* %x, align 2{{$}}
+    *x
+}
+
+// CHECK-LABEL: @load_nonzero_int
+#[no_mangle]
+pub fn load_nonzero_int(x: &NonZeroU16) -> NonZeroU16 {
+// CHECK: load i16, i16* %x, align 2, !range ![[NONZEROU16_RANGE:[0-9]+]], !noundef !{{[0-9]+}}
+    *x
+}
+
+// CHECK-LABEL: @load_option_nonzero_int
+#[no_mangle]
+pub fn load_option_nonzero_int(x: &Option<NonZeroU16>) -> Option<NonZeroU16> {
+// CHECK: load i16, i16* %x, align 2{{$}}
+    *x
 }
 
 // CHECK-LABEL: @borrow
@@ -43,3 +119,6 @@ pub fn small_struct_alignment(x: Bytes) -> Bytes {
 // CHECK: ret i32 [[VAR]]
     x
 }
+
+// CHECK: ![[BOOL_RANGE]] = !{i8 0, i8 2}
+// CHECK: ![[NONZEROU16_RANGE]] = !{i16 1, i16 0}