Skip to content

Commit

Permalink
Shrink from_raw_parts's MIR so that Vec::deref MIR-inlines again
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Mar 29, 2024
1 parent 556216a commit e27ce30
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 51 deletions.
74 changes: 74 additions & 0 deletions library/core/src/ptr/internal_repr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! This encapsulates the layout knowledge for pointers, only exporting the two
//! safe functions that can be used to interact with the metadata directly.
use super::{NonNull, Pointee, Thin};

#[inline]
pub(super) const fn metadata<P: RawPointer>(ptr: P) -> <P::Pointee as Pointee>::Metadata {
// SAFETY: Transmuting like this is safe since `P` and `PtrComponents<P, _>`
// have the same memory layouts. Only std can make this guarantee.
unsafe {
crate::intrinsics::transmute_unchecked::<
P,
PtrComponents<P::Family, <P::Pointee as Pointee>::Metadata>,
>(ptr)
.metadata
}
}

/// Just like [`from_raw_parts`] and [`from_raw_parts_mut`], but more flexible
/// in terms of which types it can take, allowing smaller MIR.
// See <https://github.com/rust-lang/rust/issues/123174>
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]
pub(super) const fn from_raw_parts<P: RawPointer>(
data_pointer: impl RawPointer<Pointee: Thin, Family = P::Family>,
metadata: <P::Pointee as Pointee>::Metadata,
) -> P {
// SAFETY: Transmuting like this is safe since `P` and `PtrComponents<P, _>`
// have the same memory layouts. Only std can make this guarantee.
unsafe {
crate::intrinsics::transmute_unchecked::<
PtrComponents<_, <P::Pointee as Pointee>::Metadata>,
P,
>(PtrComponents { data_pointer, metadata })
}
}

// Intentionally private with no derives, as it's only used via transmuting.
// This layout is not stable; only std can rely on it.
// (And should only do so in the two functions in this module.)
#[repr(C)]
struct PtrComponents<P, M = ()> {
data_pointer: P,
metadata: M,
}

/// Internal trait to avoid bad instantiations of [`PtrComponents`]
///
/// # Safety
///
/// Must have the same layout as `*const Self::Pointee` and be able to hold provenance.
///
/// Every type with the same associated `Family` must be soundly transmutable
/// between each other when the metadata is the same.
pub unsafe trait RawPointer: Copy {
type Pointee: ?Sized + super::Pointee;
type Family: RawPointer<Pointee = (), Family = Self::Family>;
}

// SAFETY: `*const T` is obviously a raw pointer
unsafe impl<T: ?Sized> RawPointer for *const T {
type Pointee = T;
type Family = *const ();
}
// SAFETY: `*mut T` is obviously a raw pointer
unsafe impl<T: ?Sized> RawPointer for *mut T {
type Pointee = T;
type Family = *mut ();
}
// SAFETY: `NonNull<T>` is a transparent newtype around a `*const T`.
unsafe impl<T: ?Sized> RawPointer for NonNull<T> {
type Pointee = T;
type Family = NonNull<()>;
}
39 changes: 4 additions & 35 deletions library/core/src/ptr/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::fmt;
use crate::hash::{Hash, Hasher};
use crate::ptr::internal_repr;

/// Provides the pointer metadata type of any pointed-to type.
///
Expand Down Expand Up @@ -92,10 +93,7 @@ pub trait Thin = Pointee<Metadata = ()>;
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]
pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
internal_repr::metadata(ptr)
}

/// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
Expand All @@ -112,10 +110,7 @@ pub const fn from_raw_parts<T: ?Sized>(
data_pointer: *const (),
metadata: <T as Pointee>::Metadata,
) -> *const T {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr }
internal_repr::from_raw_parts(data_pointer, metadata)
}

/// Performs the same functionality as [`from_raw_parts`], except that a
Expand All @@ -129,33 +124,7 @@ pub const fn from_raw_parts_mut<T: ?Sized>(
data_pointer: *mut (),
metadata: <T as Pointee>::Metadata,
) -> *mut T {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr }
}

#[repr(C)]
union PtrRepr<T: ?Sized> {
const_ptr: *const T,
mut_ptr: *mut T,
components: PtrComponents<T>,
}

#[repr(C)]
struct PtrComponents<T: ?Sized> {
data_pointer: *const (),
metadata: <T as Pointee>::Metadata,
}

// Manual impl needed to avoid `T: Copy` bound.
impl<T: ?Sized> Copy for PtrComponents<T> {}

// Manual impl needed to avoid `T: Clone` bound.
impl<T: ?Sized> Clone for PtrComponents<T> {
fn clone(&self) -> Self {
*self
}
internal_repr::from_raw_parts(data_pointer, metadata)
}

/// The metadata for a `Dyn = dyn SomeTrait` trait object type.
Expand Down
6 changes: 4 additions & 2 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ pub use crate::intrinsics::copy;
#[doc(inline)]
pub use crate::intrinsics::write_bytes;

mod internal_repr;

mod metadata;
#[unstable(feature = "ptr_metadata", issue = "81513")]
pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin};
Expand Down Expand Up @@ -812,7 +814,7 @@ pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T {
#[rustc_allow_const_fn_unstable(ptr_metadata)]
#[rustc_diagnostic_item = "ptr_slice_from_raw_parts"]
pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
from_raw_parts(data.cast(), len)
internal_repr::from_raw_parts(data, len)
}

/// Forms a raw mutable slice from a pointer and a length.
Expand Down Expand Up @@ -858,7 +860,7 @@ pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
#[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")]
#[rustc_diagnostic_item = "ptr_slice_from_raw_parts_mut"]
pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
from_raw_parts_mut(data.cast(), len)
internal_repr::from_raw_parts(data, len)
}

/// Swaps the values at two mutable locations of the same type, without
Expand Down
8 changes: 2 additions & 6 deletions library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use crate::marker::Unsize;
use crate::mem::{MaybeUninit, SizedTypeProperties};
use crate::num::NonZero;
use crate::ops::{CoerceUnsized, DispatchFromDyn};
use crate::ptr;
use crate::ptr::Unique;
use crate::ptr::{self, internal_repr, Unique};
use crate::slice::{self, SliceIndex};
use crate::ub_checks::assert_unsafe_precondition;

Expand Down Expand Up @@ -265,10 +264,7 @@ impl<T: ?Sized> NonNull<T> {
data_pointer: NonNull<()>,
metadata: <T as super::Pointee>::Metadata,
) -> NonNull<T> {
// SAFETY: The result of `ptr::from::raw_parts_mut` is non-null because `data_pointer` is.
unsafe {
NonNull::new_unchecked(super::from_raw_parts_mut(data_pointer.as_ptr(), metadata))
}
internal_repr::from_raw_parts(data_pointer, metadata)
}

/// Decompose a (possibly wide) pointer into its data pointer and metadata components.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,73 @@
fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
debug v => _1;
let mut _0: &[u8];

bb0: {
_0 = <Vec<u8> as Deref>::deref(move _1) -> [return: bb1, unwind unreachable];
scope 1 (inlined <Vec<u8> as Deref>::deref) {
debug self => _1;
let mut _4: *const u8;
let mut _5: usize;
scope 2 {
scope 3 (inlined Vec::<u8>::as_ptr) {
debug self => _1;
let mut _2: &alloc::raw_vec::RawVec<u8>;
scope 4 (inlined alloc::raw_vec::RawVec::<u8>::ptr) {
debug self => _2;
let mut _3: std::ptr::NonNull<u8>;
scope 5 (inlined Unique::<u8>::as_ptr) {
debug ((self: Unique<u8>).0: std::ptr::NonNull<u8>) => _3;
debug ((self: Unique<u8>).1: std::marker::PhantomData<u8>) => const PhantomData::<u8>;
scope 6 (inlined NonNull::<u8>::as_ptr) {
debug self => _3;
}
}
}
}
scope 7 (inlined std::slice::from_raw_parts::<'_, u8>) {
debug data => _4;
debug len => _5;
let _7: *const [u8];
scope 8 {
scope 9 (inlined core::ub_checks::check_language_ub) {
scope 10 (inlined core::ub_checks::check_language_ub::runtime) {
}
}
scope 11 (inlined std::mem::size_of::<u8>) {
}
scope 12 (inlined align_of::<u8>) {
}
scope 13 (inlined slice_from_raw_parts::<u8>) {
debug data => _4;
debug len => _5;
scope 14 (inlined std::ptr::internal_repr::from_raw_parts::<*const [u8], *const u8>) {
debug data_pointer => _4;
debug metadata => _5;
let mut _6: std::ptr::internal_repr::PtrComponents<*const u8, usize>;
scope 15 {
}
}
}
}
}
}
}

bb1: {
bb0: {
StorageLive(_4);
StorageLive(_2);
_2 = &((*_1).0: alloc::raw_vec::RawVec<u8>);
StorageLive(_3);
_3 = ((((*_1).0: alloc::raw_vec::RawVec<u8>).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
_4 = (_3.0: *const u8);
StorageDead(_3);
StorageDead(_2);
StorageLive(_5);
_5 = ((*_1).1: usize);
StorageLive(_6);
_6 = std::ptr::internal_repr::PtrComponents::<*const u8, usize> { data_pointer: _4, metadata: _5 };
_7 = move _6 as *const [u8] (Transmute);
StorageDead(_6);
StorageDead(_5);
StorageDead(_4);
_0 = &(*_7);
return;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,73 @@
fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
debug v => _1;
let mut _0: &[u8];

bb0: {
_0 = <Vec<u8> as Deref>::deref(move _1) -> [return: bb1, unwind continue];
scope 1 (inlined <Vec<u8> as Deref>::deref) {
debug self => _1;
let mut _4: *const u8;
let mut _5: usize;
scope 2 {
scope 3 (inlined Vec::<u8>::as_ptr) {
debug self => _1;
let mut _2: &alloc::raw_vec::RawVec<u8>;
scope 4 (inlined alloc::raw_vec::RawVec::<u8>::ptr) {
debug self => _2;
let mut _3: std::ptr::NonNull<u8>;
scope 5 (inlined Unique::<u8>::as_ptr) {
debug ((self: Unique<u8>).0: std::ptr::NonNull<u8>) => _3;
debug ((self: Unique<u8>).1: std::marker::PhantomData<u8>) => const PhantomData::<u8>;
scope 6 (inlined NonNull::<u8>::as_ptr) {
debug self => _3;
}
}
}
}
scope 7 (inlined std::slice::from_raw_parts::<'_, u8>) {
debug data => _4;
debug len => _5;
let _7: *const [u8];
scope 8 {
scope 9 (inlined core::ub_checks::check_language_ub) {
scope 10 (inlined core::ub_checks::check_language_ub::runtime) {
}
}
scope 11 (inlined std::mem::size_of::<u8>) {
}
scope 12 (inlined align_of::<u8>) {
}
scope 13 (inlined slice_from_raw_parts::<u8>) {
debug data => _4;
debug len => _5;
scope 14 (inlined std::ptr::internal_repr::from_raw_parts::<*const [u8], *const u8>) {
debug data_pointer => _4;
debug metadata => _5;
let mut _6: std::ptr::internal_repr::PtrComponents<*const u8, usize>;
scope 15 {
}
}
}
}
}
}
}

bb1: {
bb0: {
StorageLive(_4);
StorageLive(_2);
_2 = &((*_1).0: alloc::raw_vec::RawVec<u8>);
StorageLive(_3);
_3 = ((((*_1).0: alloc::raw_vec::RawVec<u8>).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
_4 = (_3.0: *const u8);
StorageDead(_3);
StorageDead(_2);
StorageLive(_5);
_5 = ((*_1).1: usize);
StorageLive(_6);
_6 = std::ptr::internal_repr::PtrComponents::<*const u8, usize> { data_pointer: _4, metadata: _5 };
_7 = move _6 as *const [u8] (Transmute);
StorageDead(_6);
StorageDead(_5);
StorageDead(_4);
_0 = &(*_7);
return;
}
}

0 comments on commit e27ce30

Please sign in to comment.