1 use super::{Pointer, Tag}; 2 use crate::stable_hasher::{HashStable, StableHasher}; 3 use std::fmt; 4 use std::hash::{Hash, Hasher}; 5 use std::marker::PhantomData; 6 use std::mem::ManuallyDrop; 7 use std::num::NonZeroUsize; 8 use std::ops::{Deref, DerefMut}; 9 use std::ptr::NonNull; 10 11 /// A [`Copy`] tagged pointer. 12 /// 13 /// This is essentially `{ pointer: P, tag: T }` packed in a single pointer. 14 /// 15 /// You should use this instead of the [`TaggedPtr`] type in all cases where 16 /// `P` implements [`Copy`]. 17 /// 18 /// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without 19 /// unpacking. Otherwise we don't implement [`PartialEq`], [`Eq`] and [`Hash`]; 20 /// if you want that, wrap the [`CopyTaggedPtr`]. 21 /// 22 /// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr 23 pub struct CopyTaggedPtr<P, T, const COMPARE_PACKED: bool> 24 where 25 P: Pointer, 26 T: Tag, 27 { 28 /// This is semantically a pair of `pointer: P` and `tag: T` fields, 29 /// however we pack them in a single pointer, to save space. 30 /// 31 /// We pack the tag into the **most**-significant bits of the pointer to 32 /// ease retrieval of the value. A left shift is a multiplication and 33 /// those are embeddable in instruction encoding, for example: 34 /// 35 /// ```asm 36 /// // (<https://godbolt.org/z/jqcYPWEr3>) 37 /// example::shift_read3: 38 /// mov eax, dword ptr [8*rdi] 39 /// ret 40 /// 41 /// example::mask_read3: 42 /// and rdi, -8 43 /// mov eax, dword ptr [rdi] 44 /// ret 45 /// ``` 46 /// 47 /// This is ASM outputted by rustc for reads of values behind tagged 48 /// pointers for different approaches of tagging: 49 /// - `shift_read3` uses `<< 3` (the tag is in the most-significant bits) 50 /// - `mask_read3` uses `& !0b111` (the tag is in the least-significant bits) 51 /// 52 /// The shift approach thus produces less instructions and is likely faster 53 /// (see <https://godbolt.org/z/Y913sMdWb>). 54 /// 55 /// Encoding diagram: 56 /// ```text 57 /// [ packed.addr ] 58 /// [ tag ] [ pointer.addr >> T::BITS ] <-- usize::BITS - T::BITS bits 59 /// ^ 60 /// | 61 /// T::BITS bits 62 /// ``` 63 /// 64 /// The tag can be retrieved by `packed.addr() >> T::BITS` and the pointer 65 /// can be retrieved by `packed.map_addr(|addr| addr << T::BITS)`. 66 packed: NonNull<P::Target>, 67 tag_ghost: PhantomData<T>, 68 } 69 70 // Note that even though `CopyTaggedPtr` is only really expected to work with 71 // `P: Copy`, can't add `P: Copy` bound, because `CopyTaggedPtr` is used in the 72 // `TaggedPtr`'s implementation. 73 impl<P, T, const CP: bool> CopyTaggedPtr<P, T, CP> 74 where 75 P: Pointer, 76 T: Tag, 77 { 78 /// Tags `pointer` with `tag`. 79 /// 80 /// Note that this leaks `pointer`: it won't be dropped when 81 /// `CopyTaggedPtr` is dropped. If you have a pointer with a significant 82 /// drop, use [`TaggedPtr`] instead. 83 /// 84 /// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr 85 #[inline] new(pointer: P, tag: T) -> Self86 pub fn new(pointer: P, tag: T) -> Self { 87 Self { packed: Self::pack(P::into_ptr(pointer), tag), tag_ghost: PhantomData } 88 } 89 90 /// Retrieves the pointer. 91 #[inline] pointer(self) -> P where P: Copy,92 pub fn pointer(self) -> P 93 where 94 P: Copy, 95 { 96 // SAFETY: pointer_raw returns the original pointer 97 // 98 // Note that this isn't going to double-drop or anything because we have 99 // P: Copy 100 unsafe { P::from_ptr(self.pointer_raw()) } 101 } 102 103 /// Retrieves the tag. 104 #[inline] tag(&self) -> T105 pub fn tag(&self) -> T { 106 // Unpack the tag, according to the `self.packed` encoding scheme 107 let tag = self.packed.addr().get() >> Self::TAG_BIT_SHIFT; 108 109 // Safety: 110 // The shift retrieves the original value from `T::into_usize`, 111 // satisfying `T::from_usize`'s preconditions. 112 unsafe { T::from_usize(tag) } 113 } 114 115 /// Sets the tag to a new value. 116 #[inline] set_tag(&mut self, tag: T)117 pub fn set_tag(&mut self, tag: T) { 118 self.packed = Self::pack(self.pointer_raw(), tag); 119 } 120 121 const TAG_BIT_SHIFT: u32 = usize::BITS - T::BITS; 122 const ASSERTION: () = { assert!(T::BITS <= P::BITS) }; 123 124 /// Pack pointer `ptr` that comes from [`P::into_ptr`] with a `tag`, 125 /// according to `self.packed` encoding scheme. 126 /// 127 /// [`P::into_ptr`]: Pointer::into_ptr 128 #[inline] pack(ptr: NonNull<P::Target>, tag: T) -> NonNull<P::Target>129 fn pack(ptr: NonNull<P::Target>, tag: T) -> NonNull<P::Target> { 130 // Trigger assert! 131 let () = Self::ASSERTION; 132 133 let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT; 134 135 ptr.map_addr(|addr| { 136 // Safety: 137 // - The pointer is `NonNull` => it's address is `NonZeroUsize` 138 // - `P::BITS` least significant bits are always zero (`Pointer` contract) 139 // - `T::BITS <= P::BITS` (from `Self::ASSERTION`) 140 // 141 // Thus `addr >> T::BITS` is guaranteed to be non-zero. 142 // 143 // `{non_zero} | packed_tag` can't make the value zero. 144 145 let packed = (addr.get() >> T::BITS) | packed_tag; 146 unsafe { NonZeroUsize::new_unchecked(packed) } 147 }) 148 } 149 150 /// Retrieves the original raw pointer from `self.packed`. 151 #[inline] pointer_raw(&self) -> NonNull<P::Target>152 pub(super) fn pointer_raw(&self) -> NonNull<P::Target> { 153 self.packed.map_addr(|addr| unsafe { NonZeroUsize::new_unchecked(addr.get() << T::BITS) }) 154 } 155 156 /// This provides a reference to the `P` pointer itself, rather than the 157 /// `Deref::Target`. It is used for cases where we want to call methods 158 /// that may be implement differently for the Pointer than the Pointee 159 /// (e.g., `Rc::clone` vs cloning the inner value). with_pointer_ref<R>(&self, f: impl FnOnce(&P) -> R) -> R160 pub(super) fn with_pointer_ref<R>(&self, f: impl FnOnce(&P) -> R) -> R { 161 // Safety: 162 // - `self.raw.pointer_raw()` is originally returned from `P::into_ptr` 163 // and as such is valid for `P::from_ptr`. 164 // - This also allows us to not care whatever `f` panics or not. 165 // - Even though we create a copy of the pointer, we store it inside 166 // `ManuallyDrop` and only access it by-ref, so we don't double-drop. 167 // 168 // Semantically this is just `f(&self.pointer)` (where `self.pointer` 169 // is non-packed original pointer). 170 // 171 // Note that even though `CopyTaggedPtr` is only really expected to 172 // work with `P: Copy`, we have to assume `P: ?Copy`, because 173 // `CopyTaggedPtr` is used in the `TaggedPtr`'s implementation. 174 let ptr = unsafe { ManuallyDrop::new(P::from_ptr(self.pointer_raw())) }; 175 f(&ptr) 176 } 177 } 178 179 impl<P, T, const CP: bool> Copy for CopyTaggedPtr<P, T, CP> 180 where 181 P: Pointer + Copy, 182 T: Tag, 183 { 184 } 185 186 impl<P, T, const CP: bool> Clone for CopyTaggedPtr<P, T, CP> 187 where 188 P: Pointer + Copy, 189 T: Tag, 190 { 191 #[inline] clone(&self) -> Self192 fn clone(&self) -> Self { 193 *self 194 } 195 } 196 197 impl<P, T, const CP: bool> Deref for CopyTaggedPtr<P, T, CP> 198 where 199 P: Pointer, 200 T: Tag, 201 { 202 type Target = P::Target; 203 204 #[inline] deref(&self) -> &Self::Target205 fn deref(&self) -> &Self::Target { 206 // Safety: 207 // `pointer_raw` returns the original pointer from `P::into_ptr` which, 208 // by the `Pointer`'s contract, must be valid. 209 unsafe { self.pointer_raw().as_ref() } 210 } 211 } 212 213 impl<P, T, const CP: bool> DerefMut for CopyTaggedPtr<P, T, CP> 214 where 215 P: Pointer + DerefMut, 216 T: Tag, 217 { 218 #[inline] deref_mut(&mut self) -> &mut Self::Target219 fn deref_mut(&mut self) -> &mut Self::Target { 220 // Safety: 221 // `pointer_raw` returns the original pointer from `P::into_ptr` which, 222 // by the `Pointer`'s contract, must be valid for writes if 223 // `P: DerefMut`. 224 unsafe { self.pointer_raw().as_mut() } 225 } 226 } 227 228 impl<P, T, const CP: bool> fmt::Debug for CopyTaggedPtr<P, T, CP> 229 where 230 P: Pointer + fmt::Debug, 231 T: Tag + fmt::Debug, 232 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 234 self.with_pointer_ref(|ptr| { 235 f.debug_struct("CopyTaggedPtr").field("pointer", ptr).field("tag", &self.tag()).finish() 236 }) 237 } 238 } 239 240 impl<P, T> PartialEq for CopyTaggedPtr<P, T, true> 241 where 242 P: Pointer, 243 T: Tag, 244 { 245 #[inline] eq(&self, other: &Self) -> bool246 fn eq(&self, other: &Self) -> bool { 247 self.packed == other.packed 248 } 249 } 250 251 impl<P, T> Eq for CopyTaggedPtr<P, T, true> 252 where 253 P: Pointer, 254 T: Tag, 255 { 256 } 257 258 impl<P, T> Hash for CopyTaggedPtr<P, T, true> 259 where 260 P: Pointer, 261 T: Tag, 262 { 263 #[inline] hash<H: Hasher>(&self, state: &mut H)264 fn hash<H: Hasher>(&self, state: &mut H) { 265 self.packed.hash(state); 266 } 267 } 268 269 impl<P, T, HCX, const CP: bool> HashStable<HCX> for CopyTaggedPtr<P, T, CP> 270 where 271 P: Pointer + HashStable<HCX>, 272 T: Tag + HashStable<HCX>, 273 { hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher)274 fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { 275 self.with_pointer_ref(|ptr| ptr.hash_stable(hcx, hasher)); 276 self.tag().hash_stable(hcx, hasher); 277 } 278 } 279 280 // Safety: 281 // `CopyTaggedPtr<P, T, ..>` is semantically just `{ ptr: P, tag: T }`, as such 282 // it's ok to implement `Sync` as long as `P: Sync, T: Sync` 283 unsafe impl<P, T, const CP: bool> Sync for CopyTaggedPtr<P, T, CP> 284 where 285 P: Sync + Pointer, 286 T: Sync + Tag, 287 { 288 } 289 290 // Safety: 291 // `CopyTaggedPtr<P, T, ..>` is semantically just `{ ptr: P, tag: T }`, as such 292 // it's ok to implement `Send` as long as `P: Send, T: Send` 293 unsafe impl<P, T, const CP: bool> Send for CopyTaggedPtr<P, T, CP> 294 where 295 P: Send + Pointer, 296 T: Send + Tag, 297 { 298 } 299 300 /// Test that `new` does not compile if there is not enough alignment for the 301 /// tag in the pointer. 302 /// 303 /// ```compile_fail,E0080 304 /// use rustc_data_structures::tagged_ptr::{CopyTaggedPtr, Tag}; 305 /// 306 /// #[derive(Copy, Clone, Debug, PartialEq, Eq)] 307 /// enum Tag2 { B00 = 0b00, B01 = 0b01, B10 = 0b10, B11 = 0b11 }; 308 /// 309 /// unsafe impl Tag for Tag2 { 310 /// const BITS: u32 = 2; 311 /// 312 /// fn into_usize(self) -> usize { todo!() } 313 /// unsafe fn from_usize(tag: usize) -> Self { todo!() } 314 /// } 315 /// 316 /// let value = 12u16; 317 /// let reference = &value; 318 /// let tag = Tag2::B01; 319 /// 320 /// let _ptr = CopyTaggedPtr::<_, _, true>::new(reference, tag); 321 /// ``` 322 // For some reason miri does not get the compile error 323 // probably it `check`s instead of `build`ing? 324 #[cfg(not(miri))] 325 const _: () = (); 326 327 #[cfg(test)] 328 mod tests; 329