Skip to content

Commit cbea962

Browse files
committed
MaybeUninit::assume_init_read should have noundef load metadata
I was looking into `array::IntoIter` optimization, and noticed that it wasn't annotating the loads with `noundef` for simple things like `array::IntoIter<i32, N>`. Turned out to be a more general problem as `MaybeUninit::assume_init_read` isn't marking the load as initialized (<https://rust.godbolt.org/z/Mxd8TPTnv>), which is unfortunate since that's basically its reason to exist. This PR lowers `ptr::read(p)` to `copy *p` in MIR, which fortuitiously also improves the IR we give to LLVM for things like `mem::replace`.
1 parent d924658 commit cbea962

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

core/src/intrinsics.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,20 @@ extern "rust-intrinsic" {
20202020
#[rustc_safe_intrinsic]
20212021
pub fn saturating_sub<T: Copy>(a: T, b: T) -> T;
20222022

2023+
/// This is a *typed* read, `copy *p` in MIR.
2024+
///
2025+
/// The stabilized form of this intrinsic is [`crate::ptr::read`], so
2026+
/// that can be implemented without needing to do an *untyped* copy
2027+
/// via [`copy_nonoverlapping`], and thus can get proper metadata.
2028+
///
2029+
/// This intrinsic can *only* be called with a copy or move of a local.
2030+
/// (It allows neither constants nor projections.)
2031+
///
2032+
/// To avoid introducing any `noalias` requirements, it just takes a pointer.
2033+
#[cfg(not(bootstrap))]
2034+
#[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")]
2035+
pub fn read_via_copy<T>(p: *const T) -> T;
2036+
20232037
/// Returns the value of the discriminant for the variant in 'v';
20242038
/// if `T` has no discriminant, returns `0`.
20252039
///

core/src/ptr/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,25 +1137,33 @@ pub const unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
11371137
pub const unsafe fn read<T>(src: *const T) -> T {
11381138
// We are calling the intrinsics directly to avoid function calls in the generated code
11391139
// as `intrinsics::copy_nonoverlapping` is a wrapper function.
1140+
#[cfg(bootstrap)]
11401141
extern "rust-intrinsic" {
11411142
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")]
11421143
fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
11431144
}
11441145

1145-
let mut tmp = MaybeUninit::<T>::uninit();
11461146
// SAFETY: the caller must guarantee that `src` is valid for reads.
11471147
// `src` cannot overlap `tmp` because `tmp` was just allocated on
11481148
// the stack as a separate allocated object.
1149-
//
1150-
// Also, since we just wrote a valid value into `tmp`, it is guaranteed
1151-
// to be properly initialized.
11521149
unsafe {
11531150
assert_unsafe_precondition!(
11541151
"ptr::read requires that the pointer argument is aligned and non-null",
11551152
[T](src: *const T) => is_aligned_and_not_null(src)
11561153
);
1157-
copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
1158-
tmp.assume_init()
1154+
1155+
#[cfg(bootstrap)]
1156+
{
1157+
let mut tmp = MaybeUninit::<T>::uninit();
1158+
copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
1159+
tmp.assume_init()
1160+
}
1161+
#[cfg(not(bootstrap))]
1162+
{
1163+
// This uses a dedicated intrinsic, not `copy_nonoverlapping`,
1164+
// so that it gets a *typed* copy, not an *untyped* one.
1165+
crate::intrinsics::read_via_copy(src)
1166+
}
11591167
}
11601168
}
11611169

0 commit comments

Comments
 (0)