From d6f82713fc9deb0c1e615d32074286c056ac57e2 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 5 Jul 2022 20:39:58 -0400 Subject: [PATCH] Revert #94158, "Apply noundef metadata to loads of types that do not permit raw init" In #94158, we started emitting `noundef`, which means that functions returning uninitialized references emit IR with is both `ret void` and also `noundef`: https://godbolt.org/z/hbjsKKfc3 More generally, this change makes `mem::uninitialized` dangerous in a way that it wasn't before. This `noundef` change was shipped in the past 2 stable releases, 1.61.0 and 1.62.0. This concerns me because in #66151 we have thus far decided against issuing a panic for creating uninitialized references within arrays, on account of the breakage that shows up in crater. If this pattern is so widely-used that we're not willing to put in a runtime check for it, then it doesn't seem prudent to invite LLVM to exploit this UB. The pattern of creating uninit references in arrays shows up real code because the 0.11, 0.12, and 0.13 release series for `hyper` all use `mem::uninitialized` to construct arrays of `httparse::Header`, which contains a `&str` and `&[u8]`. There is no patch available within these release series, so a `cargo update` is not a viable way to escape the UB here. Also note that the 0.11 release series of `hyper` predates `MaybeUninit`, so any source-level patch for that will incur runtime overhead. Which would be unfortunate, but preferable to UB. I discussed this with @thomcc on the community Discord, and we think that it would be prudent to revert this introduction of `noundef` until we have a mitigation in place for the UB that this may unleash. We haven't been able to cook up any examples of surprising optimizations due to this pattern, but the whole point of `noundef` is to enable optimizations, and we know that there is code which uses it in a way which is definitely instant UB and which we have declined to inform users of. If possible, we would like to see `freeze` applied to the return value of `mem::uninitialized` as a mitigation for this problem. That may be able to keep old code functioning without introducing a performance hit. @rustbot labels add +T-compiler +I-compiler-nominated --- compiler/rustc_codegen_llvm/src/builder.rs | 14 ---- src/test/codegen/loads.rs | 95 ---------------------- 2 files changed, 109 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4a4cccb490d1c..aadaf79a95423 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -480,10 +480,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { layout: TyAndLayout<'tcx>, offset: Size, ) { - if !scalar.is_always_valid(bx) { - bx.noundef_metadata(load); - } - match scalar.primitive() { abi::Int(..) => { if !scalar.is_always_valid(bx) { @@ -1249,16 +1245,6 @@ 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/src/test/codegen/loads.rs b/src/test/codegen/loads.rs index f448306ba1b08..47eb967bc4534 100644 --- a/src/test/codegen/loads.rs +++ b/src/test/codegen/loads.rs @@ -2,9 +2,6 @@ #![crate_type = "lib"] -use std::mem::MaybeUninit; -use std::num::NonZeroU16; - pub struct Bytes { a: u8, b: u8, @@ -12,41 +9,10 @@ pub struct Bytes { d: u8, } -#[derive(Copy, Clone)] -pub enum MyBool { - True, - False, -} - -#[repr(align(16))] -pub struct Align16(u128); - // CHECK: @ptr_alignment_helper({{.*}}align [[PTR_ALIGNMENT:[0-9]+]] #[no_mangle] pub fn ptr_alignment_helper(x: &&()) {} -// CHECK-LABEL: @load_ref -#[no_mangle] -pub fn load_ref<'a>(x: &&'a i32) -> &'a i32 { - // CHECK: load {{i32\*|ptr}}, {{i32\*\*|ptr}} %x, align [[PTR_ALIGNMENT]], !nonnull !{{[0-9]+}}, !align ![[ALIGN_4_META:[0-9]+]], !noundef !{{[0-9]+}} - *x -} - -// CHECK-LABEL: @load_ref_higher_alignment -#[no_mangle] -pub fn load_ref_higher_alignment<'a>(x: &&'a Align16) -> &'a Align16 { - // CHECK: load {{%Align16\*|i128\*|ptr}}, {{%Align16\*\*|i128\*\*|ptr}} %x, align [[PTR_ALIGNMENT]], !nonnull !{{[0-9]+}}, !align ![[ALIGN_16_META:[0-9]+]], !noundef !{{[0-9]+}} - *x -} - -// CHECK-LABEL: @load_scalar_pair -#[no_mangle] -pub fn load_scalar_pair<'a>(x: &(&'a i32, &'a Align16)) -> (&'a i32, &'a Align16) { - // CHECK: load {{i32\*|ptr}}, {{i32\*\*|ptr}} %{{.+}}, align [[PTR_ALIGNMENT]], !nonnull !{{[0-9]+}}, !align ![[ALIGN_4_META]], !noundef !{{[0-9]+}} - // CHECK: load {{i64\*|ptr}}, {{i64\*\*|ptr}} %{{.+}}, align [[PTR_ALIGNMENT]], !nonnull !{{[0-9]+}}, !align ![[ALIGN_16_META]], !noundef !{{[0-9]+}} - *x -} - // CHECK-LABEL: @load_raw_pointer #[no_mangle] pub fn load_raw_pointer<'a>(x: &*const i32) -> *const i32 { @@ -55,62 +21,6 @@ pub fn load_raw_pointer<'a>(x: &*const i32) -> *const i32 { *x } -// CHECK-LABEL: @load_box -#[no_mangle] -pub fn load_box<'a>(x: Box>) -> Box { - // CHECK: load {{i32\*|ptr}}, {{i32\*\*|ptr}} %{{.*}}, align [[PTR_ALIGNMENT]], !nonnull !{{[0-9]+}}, !align ![[ALIGN_4_META]], !noundef !{{[0-9]+}} - *x -} - -// CHECK-LABEL: @load_bool -#[no_mangle] -pub fn load_bool(x: &bool) -> bool { - // CHECK: load i8, {{i8\*|ptr}} %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\*|ptr}} %x, align 1{{$}} - *x -} - -// CHECK-LABEL: @load_enum_bool -#[no_mangle] -pub fn load_enum_bool(x: &MyBool) -> MyBool { - // CHECK: load i8, {{i8\*|ptr}} %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\*|ptr}} %x, align 1{{$}} - *x -} - -// CHECK-LABEL: @load_int -#[no_mangle] -pub fn load_int(x: &u16) -> u16 { - // CHECK: load i16, {{i16\*|ptr}} %x, align 2{{$}} - *x -} - -// CHECK-LABEL: @load_nonzero_int -#[no_mangle] -pub fn load_nonzero_int(x: &NonZeroU16) -> NonZeroU16 { - // CHECK: load i16, {{i16\*|ptr}} %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\*|ptr}} %x, align 2{{$}} - *x -} - // CHECK-LABEL: @borrow #[no_mangle] pub fn borrow(x: &i32) -> &i32 { @@ -145,8 +55,3 @@ pub fn small_struct_alignment(x: Bytes) -> Bytes { // CHECK: ret i32 [[VAR]] x } - -// CHECK-DAG: ![[BOOL_RANGE]] = !{i8 0, i8 2} -// CHECK-DAG: ![[NONZEROU16_RANGE]] = !{i16 1, i16 0} -// CHECK-DAG: ![[ALIGN_4_META]] = !{i64 4} -// CHECK-DAG: ![[ALIGN_16_META]] = !{i64 16}