rustc_data_structures/tagged_ptr.rs
1//! This module implements tagged pointers. In order to utilize the pointer
2//! packing, you must have a tag type implementing the [`Tag`] trait.
3//!
4//! We assert that the tag and the reference type is compatible at compile
5//! time.
6
7use std::fmt;
8use std::hash::{Hash, Hasher};
9use std::marker::PhantomData;
10use std::num::NonZero;
11use std::ops::Deref;
12use std::ptr::NonNull;
13
14use crate::aligned::Aligned;
15use crate::stable_hasher::{HashStable, StableHasher};
16
17/// This describes tags that the [`TaggedRef`] struct can hold.
18///
19/// # Safety
20///
21/// - The [`BITS`] constant must be correct.
22/// - No more than [`BITS`] least-significant bits may be set in the returned usize.
23/// - [`Eq`] and [`Hash`] must be implementable with the returned `usize` from `into_usize`.
24///
25/// [`BITS`]: Tag::BITS
26pub unsafe trait Tag: Copy {
27 /// Number of least-significant bits in the return value of [`into_usize`]
28 /// which may be non-zero. In other words this is the bit width of the
29 /// value.
30 ///
31 /// [`into_usize`]: Tag::into_usize
32 const BITS: u32;
33
34 /// Turns this tag into an integer.
35 ///
36 /// The inverse of this function is [`from_usize`].
37 ///
38 /// This function guarantees that only the least-significant [`Self::BITS`]
39 /// bits can be non-zero.
40 ///
41 /// [`from_usize`]: Tag::from_usize
42 /// [`Self::BITS`]: Tag::BITS
43 fn into_usize(self) -> usize;
44
45 /// Re-creates the tag from the integer returned by [`into_usize`].
46 ///
47 /// # Safety
48 ///
49 /// The passed `tag` must be returned from [`into_usize`].
50 ///
51 /// [`into_usize`]: Tag::into_usize
52 unsafe fn from_usize(tag: usize) -> Self;
53}
54
55/// Returns the number of bits available for use for tags in a pointer to `T`
56/// (this is based on `T`'s alignment).
57pub const fn bits_for<T: ?Sized + Aligned>() -> u32 {
58 let alignment = crate::aligned::align_of::<T>();
59 #[cfg(bootstrap)]
60 let alignment = alignment.as_nonzero();
61 #[cfg(not(bootstrap))]
62 let alignment = alignment.as_nonzero_usize();
63 alignment.trailing_zeros()
64}
65
66/// Returns the correct [`Tag::BITS`] constant for a set of tag values.
67pub const fn bits_for_tags(mut tags: &[usize]) -> u32 {
68 let mut bits = 0;
69
70 while let &[tag, ref rest @ ..] = tags {
71 tags = rest;
72
73 // bits required to represent `tag`,
74 // position of the most significant 1
75 let b = usize::BITS - tag.leading_zeros();
76 if b > bits {
77 bits = b;
78 }
79 }
80
81 bits
82}
83
84/// A covariant [`Copy`] tagged borrow. This is essentially `{ pointer: &'a P, tag: T }` packed
85/// in a single reference.
86pub struct TaggedRef<'a, Pointee: Aligned + ?Sized, T: Tag> {
87 /// This is semantically a pair of `pointer: &'a P` and `tag: T` fields,
88 /// however we pack them in a single pointer, to save space.
89 ///
90 /// We pack the tag into the **most**-significant bits of the pointer to
91 /// ease retrieval of the value. A left shift is a multiplication and
92 /// those are embeddable in instruction encoding, for example:
93 ///
94 /// ```asm
95 /// // (<https://godbolt.org/z/jqcYPWEr3>)
96 /// example::shift_read3:
97 /// mov eax, dword ptr [8*rdi]
98 /// ret
99 ///
100 /// example::mask_read3:
101 /// and rdi, -8
102 /// mov eax, dword ptr [rdi]
103 /// ret
104 /// ```
105 ///
106 /// This is ASM outputted by rustc for reads of values behind tagged
107 /// pointers for different approaches of tagging:
108 /// - `shift_read3` uses `<< 3` (the tag is in the most-significant bits)
109 /// - `mask_read3` uses `& !0b111` (the tag is in the least-significant bits)
110 ///
111 /// The shift approach thus produces less instructions and is likely faster
112 /// (see <https://godbolt.org/z/Y913sMdWb>).
113 ///
114 /// Encoding diagram:
115 /// ```text
116 /// [ packed.addr ]
117 /// [ tag ] [ pointer.addr >> T::BITS ] <-- usize::BITS - T::BITS bits
118 /// ^
119 /// |
120 /// T::BITS bits
121 /// ```
122 ///
123 /// The tag can be retrieved by `packed.addr() >> T::BITS` and the pointer
124 /// can be retrieved by `packed.map_addr(|addr| addr << T::BITS)`.
125 packed: NonNull<Pointee>,
126 tag_pointer_ghost: PhantomData<(&'a Pointee, T)>,
127}
128
129impl<'a, P, T> TaggedRef<'a, P, T>
130where
131 P: Aligned + ?Sized,
132 T: Tag,
133{
134 /// Tags `pointer` with `tag`.
135 ///
136 /// [`TaggedRef`]: crate::tagged_ptr::TaggedRef
137 #[inline]
138 pub fn new(pointer: &'a P, tag: T) -> Self {
139 Self { packed: Self::pack(NonNull::from(pointer), tag), tag_pointer_ghost: PhantomData }
140 }
141
142 /// Retrieves the pointer.
143 #[inline]
144 pub fn pointer(self) -> &'a P {
145 // SAFETY: pointer_raw returns the original pointer
146 unsafe { self.pointer_raw().as_ref() }
147 }
148
149 /// Retrieves the tag.
150 #[inline]
151 pub fn tag(&self) -> T {
152 // Unpack the tag, according to the `self.packed` encoding scheme
153 let tag = self.packed.addr().get() >> Self::TAG_BIT_SHIFT;
154
155 // Safety:
156 // The shift retrieves the original value from `T::into_usize`,
157 // satisfying `T::from_usize`'s preconditions.
158 unsafe { T::from_usize(tag) }
159 }
160
161 /// Sets the tag to a new value.
162 #[inline]
163 pub fn set_tag(&mut self, tag: T) {
164 self.packed = Self::pack(self.pointer_raw(), tag);
165 }
166
167 const TAG_BIT_SHIFT: u32 = usize::BITS - T::BITS;
168 const ASSERTION: () = { assert!(T::BITS <= bits_for::<P>()) };
169
170 /// Pack pointer `ptr` with a `tag`, according to `self.packed` encoding scheme.
171 #[inline]
172 fn pack(ptr: NonNull<P>, tag: T) -> NonNull<P> {
173 // Trigger assert!
174 let () = Self::ASSERTION;
175
176 let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT;
177
178 ptr.map_addr(|addr| {
179 // Safety:
180 // - The pointer is `NonNull` => it's address is `NonZero<usize>`
181 // - `P::BITS` least significant bits are always zero (`Pointer` contract)
182 // - `T::BITS <= P::BITS` (from `Self::ASSERTION`)
183 //
184 // Thus `addr >> T::BITS` is guaranteed to be non-zero.
185 //
186 // `{non_zero} | packed_tag` can't make the value zero.
187
188 let packed = (addr.get() >> T::BITS) | packed_tag;
189 unsafe { NonZero::new_unchecked(packed) }
190 })
191 }
192
193 /// Retrieves the original raw pointer from `self.packed`.
194 #[inline]
195 pub(super) fn pointer_raw(&self) -> NonNull<P> {
196 self.packed.map_addr(|addr| unsafe { NonZero::new_unchecked(addr.get() << T::BITS) })
197 }
198}
199
200impl<P, T> Copy for TaggedRef<'_, P, T>
201where
202 P: Aligned + ?Sized,
203 T: Tag,
204{
205}
206
207impl<P, T> Clone for TaggedRef<'_, P, T>
208where
209 P: Aligned + ?Sized,
210 T: Tag,
211{
212 #[inline]
213 fn clone(&self) -> Self {
214 *self
215 }
216}
217
218impl<P, T> Deref for TaggedRef<'_, P, T>
219where
220 P: Aligned + ?Sized,
221 T: Tag,
222{
223 type Target = P;
224
225 #[inline]
226 fn deref(&self) -> &Self::Target {
227 self.pointer()
228 }
229}
230
231impl<P, T> fmt::Debug for TaggedRef<'_, P, T>
232where
233 P: Aligned + fmt::Debug + ?Sized,
234 T: Tag + fmt::Debug,
235{
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 f.debug_struct("TaggedRef")
238 .field("pointer", &self.pointer())
239 .field("tag", &self.tag())
240 .finish()
241 }
242}
243
244impl<P, T> PartialEq for TaggedRef<'_, P, T>
245where
246 P: Aligned + ?Sized,
247 T: Tag,
248{
249 #[inline]
250 #[allow(ambiguous_wide_pointer_comparisons)]
251 fn eq(&self, other: &Self) -> bool {
252 self.packed == other.packed
253 }
254}
255
256impl<P, T: Tag> Eq for TaggedRef<'_, P, T> {}
257
258impl<P, T: Tag> Hash for TaggedRef<'_, P, T> {
259 #[inline]
260 fn hash<H: Hasher>(&self, state: &mut H) {
261 self.packed.hash(state);
262 }
263}
264
265impl<'a, P, T, HCX> HashStable<HCX> for TaggedRef<'a, P, T>
266where
267 P: HashStable<HCX> + Aligned + ?Sized,
268 T: Tag + HashStable<HCX>,
269{
270 fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
271 self.pointer().hash_stable(hcx, hasher);
272 self.tag().hash_stable(hcx, hasher);
273 }
274}
275
276// Safety:
277// `TaggedRef<P, T, ..>` is semantically just `{ ptr: P, tag: T }`, as such
278// it's ok to implement `Sync` as long as `P: Sync, T: Sync`
279unsafe impl<P, T> Sync for TaggedRef<'_, P, T>
280where
281 P: Sync + Aligned + ?Sized,
282 T: Sync + Tag,
283{
284}
285
286// Safety:
287// `TaggedRef<P, T, ..>` is semantically just `{ ptr: P, tag: T }`, as such
288// it's ok to implement `Send` as long as `P: Send, T: Send`
289unsafe impl<P, T> Send for TaggedRef<'_, P, T>
290where
291 P: Sync + Aligned + ?Sized,
292 T: Send + Tag,
293{
294}
295
296#[cfg(test)]
297mod tests;