@@ -1135,17 +1135,31 @@ pub const unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
1135
1135
#[ rustc_const_unstable( feature = "const_ptr_read" , issue = "80377" ) ]
1136
1136
#[ cfg_attr( miri, track_caller) ] // even without panics, this helps for Miri backtraces
1137
1137
pub const unsafe fn read < T > ( src : * const T ) -> T {
1138
- // We are calling the intrinsics directly to avoid function calls in the generated code
1139
- // as `intrinsics::copy_nonoverlapping` is a wrapper function.
1140
- #[ cfg( bootstrap) ]
1141
- extern "rust-intrinsic" {
1142
- #[ rustc_const_stable( feature = "const_intrinsic_copy" , since = "1.63.0" ) ]
1143
- fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
1144
- }
1138
+ // It would be semantically correct to implement this via `copy_nonoverlapping`
1139
+ // and `MaybeUninit`, as was done before PR #109035.
1140
+
1141
+ // However, it switched to intrinsic that lowers to `_0 = *src` in MIR in
1142
+ // order to address a few implementation issues:
1143
+ //
1144
+ // - Using `MaybeUninit::assume_init` after a `copy_nonoverlapping` was not
1145
+ // turning the untyped copy into a typed load. As such, the generated
1146
+ // `load` in LLVM didn't get various metadata, such as `!range` (#73258),
1147
+ // `!nonnull`, and `!noundef`, resulting in poorer optimization.
1148
+ // - Going through the extra local resulted in multiple extra copies, even
1149
+ // in optimized MIR. (Ignoring StorageLive/Dead, the intrinsic is one
1150
+ // MIR statement, while the previous implementation was eight.) LLVM
1151
+ // could sometimes optimize them away, but because `read` is at the core
1152
+ // of so many things, not having them in the first place improves what we
1153
+ // hand off to the backend. For example, `mem::replace::<Big>` previously
1154
+ // emitted 4 `alloca` and 6 `memcpy`s, but is now 1 `alloc` and 3 `memcpy`s.
1155
+ // - In general, this approach keeps us from getting any more bugs (like
1156
+ // #106369) that boil down to "`read(p)` is worse than `*p`", as this
1157
+ // makes them look identical to the backend (or other MIR consumers).
1158
+ //
1159
+ // Future enhancements to MIR optimizations might well allow this to return
1160
+ // to the previous implementation, rather than using an intrinsic.
1145
1161
1146
1162
// SAFETY: the caller must guarantee that `src` is valid for reads.
1147
- // `src` cannot overlap `tmp` because `tmp` was just allocated on
1148
- // the stack as a separate allocated object.
1149
1163
unsafe {
1150
1164
assert_unsafe_precondition ! (
1151
1165
"ptr::read requires that the pointer argument is aligned and non-null" ,
@@ -1154,14 +1168,21 @@ pub const unsafe fn read<T>(src: *const T) -> T {
1154
1168
1155
1169
#[ cfg( bootstrap) ]
1156
1170
{
1171
+ // We are calling the intrinsics directly to avoid function calls in the
1172
+ // generated code as `intrinsics::copy_nonoverlapping` is a wrapper function.
1173
+ extern "rust-intrinsic" {
1174
+ #[ rustc_const_stable( feature = "const_intrinsic_copy" , since = "1.63.0" ) ]
1175
+ fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
1176
+ }
1177
+
1178
+ // `src` cannot overlap `tmp` because `tmp` was just allocated on
1179
+ // the stack as a separate allocated object.
1157
1180
let mut tmp = MaybeUninit :: < T > :: uninit ( ) ;
1158
1181
copy_nonoverlapping ( src, tmp. as_mut_ptr ( ) , 1 ) ;
1159
1182
tmp. assume_init ( )
1160
1183
}
1161
1184
#[ cfg( not( bootstrap) ) ]
1162
1185
{
1163
- // This uses a dedicated intrinsic, not `copy_nonoverlapping`,
1164
- // so that it gets a *typed* copy, not an *untyped* one.
1165
1186
crate :: intrinsics:: read_via_copy ( src)
1166
1187
}
1167
1188
}
0 commit comments