diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5e78d6fc851a5..77aaa3010f2e6 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 31d1460e178ba..81ae97a80cc17 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 3c9ecec2cbf58..977175a4a621d 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 { +// 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) -> MaybeUninit { +// 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) -> MaybeUninit { +// 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) -> Option { +// 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}