Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1810,22 +1810,22 @@ pub const unsafe fn read<T>(src: *const T) -> T {
pub const unsafe fn read_unaligned<T>(src: *const T) -> T {
// Always true thanks to the repr, but to demonstrate
const {
assert!(mem::offset_of!(Packed::<T>, 0) == 0);
assert!(size_of::<T>() == size_of::<Packed<T>>());
assert!(mem::offset_of!(Unaligned::<T>, 0) == 0);
assert!(size_of::<T>() == size_of::<Unaligned<T>>());
}

let src = src.cast::<Packed<T>>();
let src = src.cast::<Unaligned<T>>();
// SAFETY: the caller must guarantee that `src` is valid for reads.
// Reading it as `Packed<T>` instead of `T` reads those same bytes because
// Reading it as `Unaligned<T>` instead of `T` reads those same bytes because
// it's the same size (thus zero offset), but with alignment 1 instead.
//
// Similarly, because it's the same bytes it's sound to transmute from the
// `Packed<T>` to `T`. Transmute is a value-based (not a place-based)
// `Unaligned<T>` to `T`. Transmute is a value-based (not a place-based)
// operation that doesn't care about alignment.
unsafe {
let packed = read(src);
let unaligned = read(src);
// Can't just destructure because that's not allowed in const fn
mem::transmute_neo(packed)
mem::transmute_neo(unaligned)
}
}

Expand Down Expand Up @@ -2020,14 +2020,14 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
// Always true thanks to the repr, but to demonstrate
const {
assert!(mem::offset_of!(Packed::<T>, 0) == 0);
assert!(size_of::<T>() == size_of::<Packed<T>>());
assert!(mem::offset_of!(Unaligned::<T>, 0) == 0);
assert!(size_of::<T>() == size_of::<Unaligned<T>>());
}

let dst = dst.cast::<Packed<T>>();
let src = Packed(src);
let dst = dst.cast::<Unaligned<T>>();
let src = Unaligned(src);
// SAFETY: the caller must guarantee that `dst` is valid for writes.
// Writing it as `Packed<T>` instead of `T` writes those same bytes because
// Writing it as `Unaligned<T>` instead of `T` writes those same bytes because
// it's the same size (thus zero offset), but with alignment 1 instead.
unsafe { write(dst, src) }
}
Expand Down Expand Up @@ -2828,5 +2828,7 @@ pub macro addr_of_mut($place:expr) {
&raw mut $place
}

#[repr(C, packed)]
struct Packed<T>(T);
/// Used in [`read_unaligned`] and [`write_unaligned`] to load and store `T`
/// with alignment 1 rather than its usual `align_of::<T>()` alignment.
#[repr(Rust, packed)]
struct Unaligned<T>(T);
6 changes: 3 additions & 3 deletions tests/mir-opt/pre-codegen/unaligned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ pub unsafe fn unaligned_copy_generic<T>(src: *const T, dst: *mut T) {
// CHECK: debug src => _1;
// CHECK: debug dst => _2;
// CHECK: debug val => [[VAL:_.+]];
// CHECK: [[SRC_P:_.+]] = copy _1 as *const {{.+}}::Packed<T> (PtrToPtr);
// CHECK: [[SRC_P:_.+]] = copy _1 as *const {{.+}}::Unaligned<T> (PtrToPtr);
// CHECK: [[PACKED1:_.+]] = copy (*[[SRC_P]]);
// CHECK: [[VAL]] = copy [[PACKED1]] as T (Transmute);
// CHECK: [[DST_P:_.+]] = copy _2 as *mut {{.+}}::Packed<T> (PtrToPtr);
// CHECK: [[PACKED2:_.+]] = {{.+}}::Packed::<T>(copy [[VAL]]);
// CHECK: [[DST_P:_.+]] = copy _2 as *mut {{.+}}::Unaligned<T> (PtrToPtr);
// CHECK: [[PACKED2:_.+]] = {{.+}}::Unaligned::<T>(copy [[VAL]]);
// CHECK: (*[[DST_P]]) = copy [[PACKED2]];
// CHECK-NOT: copy_nonoverlapping
// CHECK-NOT: drop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,46 @@ fn unaligned_copy_generic(_1: *const T, _2: *mut T) -> () {
scope 1 {
debug val => _5;
scope 8 (inlined #[track_caller] write_unaligned::<T>) {
let _6: *mut std::ptr::Packed<T>;
let _6: *mut std::ptr::Unaligned<T>;
scope 9 {
let _7: std::ptr::Packed<T>;
let _7: std::ptr::Unaligned<T>;
scope 10 {
scope 12 (inlined #[track_caller] std::ptr::write::<std::ptr::Packed<T>>) {
scope 12 (inlined #[track_caller] std::ptr::write::<std::ptr::Unaligned<T>>) {
}
}
}
scope 11 (inlined std::ptr::mut_ptr::<impl *mut T>::cast::<std::ptr::Packed<T>>) {
scope 11 (inlined std::ptr::mut_ptr::<impl *mut T>::cast::<std::ptr::Unaligned<T>>) {
}
}
}
scope 2 (inlined #[track_caller] read_unaligned::<T>) {
let _3: *const std::ptr::Packed<T>;
let _3: *const std::ptr::Unaligned<T>;
scope 3 {
let _4: std::ptr::Packed<T>;
let _4: std::ptr::Unaligned<T>;
scope 4 {
scope 7 (inlined transmute_neo::<std::ptr::Packed<T>, T>) {
scope 7 (inlined transmute_neo::<std::ptr::Unaligned<T>, T>) {
}
}
scope 6 (inlined #[track_caller] std::ptr::read::<std::ptr::Packed<T>>) {
scope 6 (inlined #[track_caller] std::ptr::read::<std::ptr::Unaligned<T>>) {
}
}
scope 5 (inlined std::ptr::const_ptr::<impl *const T>::cast::<std::ptr::Packed<T>>) {
scope 5 (inlined std::ptr::const_ptr::<impl *const T>::cast::<std::ptr::Unaligned<T>>) {
}
}

bb0: {
StorageLive(_5);
StorageLive(_3);
_3 = copy _1 as *const std::ptr::Packed<T> (PtrToPtr);
_3 = copy _1 as *const std::ptr::Unaligned<T> (PtrToPtr);
StorageLive(_4);
_4 = copy (*_3);
_5 = copy _4 as T (Transmute);
StorageDead(_4);
StorageDead(_3);
StorageLive(_6);
_6 = copy _2 as *mut std::ptr::Packed<T> (PtrToPtr);
_6 = copy _2 as *mut std::ptr::Unaligned<T> (PtrToPtr);
StorageLive(_7);
_7 = std::ptr::Packed::<T>(copy _5);
_7 = std::ptr::Unaligned::<T>(copy _5);
(*_6) = copy _7;
StorageDead(_7);
StorageDead(_6);
Expand Down
Loading