From 7d5457712521fe43f81a73378a95c63706f8592f Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Thu, 15 May 2025 21:49:13 +0000 Subject: [PATCH] refactor(allocator/vec2): move `len` field from `Vec` to `RawVec` (#10883) Prepare for #9706. Move `len` field from `Vec` to `RawVec` to adapt upcoming change that reduces `len` and `cap` fields from `usize` to `u32` so that the `Vec` size can reduce from `32` to `24` without padding. Marked `Vec` as `#[repr(transparent)]` and `RawVec` as `#[repr(C)]` to easier to solve raw transfer test failures because the fields are no longer reordered, we can statically determine the offsets, as mentioned in that comment. --- crates/oxc_allocator/src/vec2/mod.rs | 58 +++++++++---------- crates/oxc_allocator/src/vec2/raw_vec.rs | 12 ++-- .../ast_tools/src/generators/raw_transfer.rs | 3 +- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/crates/oxc_allocator/src/vec2/mod.rs b/crates/oxc_allocator/src/vec2/mod.rs index 254c9291a549a..a91e704757906 100644 --- a/crates/oxc_allocator/src/vec2/mod.rs +++ b/crates/oxc_allocator/src/vec2/mod.rs @@ -541,9 +541,9 @@ macro_rules! vec { /// [`insert`]: struct.Vec.html#method.insert /// [`reserve`]: struct.Vec.html#method.reserve /// [owned slice]: https://doc.rust-lang.org/std/boxed/struct.Box.html +#[repr(transparent)] pub struct Vec<'bump, T: 'bump> { buf: RawVec<'bump, T>, - len: usize, } //////////////////////////////////////////////////////////////////////////////// @@ -566,7 +566,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// ``` #[inline] pub fn new_in(bump: &'bump Bump) -> Vec<'bump, T> { - Vec { buf: RawVec::new_in(bump), len: 0 } + Vec { buf: RawVec::new_in(bump) } } /// Constructs a new, empty `Vec<'bump, T>` with the specified capacity. @@ -603,7 +603,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// ``` #[inline] pub fn with_capacity_in(capacity: usize, bump: &'bump Bump) -> Vec<'bump, T> { - Vec { buf: RawVec::with_capacity_in(capacity, bump), len: 0 } + Vec { buf: RawVec::with_capacity_in(capacity, bump) } } /// Construct a new `Vec` from the given iterator's items. @@ -687,7 +687,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { capacity: usize, bump: &'bump Bump, ) -> Vec<'bump, T> { - Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, bump), len: length } + Vec { buf: RawVec::from_raw_parts_in(ptr, bump, capacity, length) } } /// Returns a shared reference to the allocator backing this `Vec`. @@ -748,7 +748,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// assert!(vec.capacity() >= 11); /// ``` pub fn reserve(&mut self, additional: usize) { - self.buf.reserve(self.len, additional); + self.buf.reserve(self.buf.len, additional); } /// Reserves the minimum capacity for exactly `additional` more elements to @@ -775,7 +775,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// assert!(vec.capacity() >= 11); /// ``` pub fn reserve_exact(&mut self, additional: usize) { - self.buf.reserve_exact(self.len, additional); + self.buf.reserve_exact(self.buf.len, additional); } /// Attempts to reserve capacity for at least `additional` more elements to be inserted @@ -799,7 +799,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// assert!(vec.capacity() >= 11); /// ``` pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { - self.buf.try_reserve(self.len, additional) + self.buf.try_reserve(self.buf.len, additional) } /// Attempts to reserve the minimum capacity for exactly `additional` more elements to @@ -826,7 +826,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// assert!(vec.capacity() >= 11); /// ``` pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { - self.buf.try_reserve_exact(self.len, additional) + self.buf.try_reserve_exact(self.buf.len, additional) } /// Shrinks the capacity of the vector as much as possible. @@ -848,8 +848,8 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// assert!(vec.capacity() >= 3); /// ``` pub fn shrink_to_fit(&mut self) { - if self.capacity() != self.len { - self.buf.shrink_to_fit(self.len); + if self.capacity() != self.buf.len { + self.buf.shrink_to_fit(self.buf.len); } } @@ -955,14 +955,14 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// [`clear`]: #method.clear /// [`drain`]: #method.drain pub fn truncate(&mut self, len: usize) { - let current_len = self.len; + let current_len = self.buf.len; unsafe { - let mut ptr = self.as_mut_ptr().add(self.len); + let mut ptr = self.as_mut_ptr().add(current_len); // Set the final length at the end, keeping in mind that // dropping an element might panic. Works around a missed // optimization, as seen in the following issue: // https://github.com/rust-lang/rust/issues/51802 - let mut local_len = SetLenOnDrop::new(&mut self.len); + let mut local_len = SetLenOnDrop::new(&mut self.buf.len); // drop any extra elements for _ in len..current_len { @@ -1170,7 +1170,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// ``` #[inline] pub unsafe fn set_len(&mut self, new_len: usize) { - self.len = new_len; + self.buf.len = new_len; } /// Removes an element from the vector and returns it. @@ -1205,8 +1205,8 @@ impl<'bump, T: 'bump> Vec<'bump, T> { // bounds check on hole succeeds there must be a last element (which // can be self[index] itself). let hole: *mut T = &mut self[index]; - let last = ptr::read(self.get_unchecked(self.len - 1)); - self.len -= 1; + let last = ptr::read(self.get_unchecked(self.buf.len - 1)); + self.buf.len -= 1; ptr::replace(hole, last) } } @@ -1564,13 +1564,13 @@ impl<'bump, T: 'bump> Vec<'bump, T> { pub fn push(&mut self, value: T) { // This will panic or abort if we would allocate > isize::MAX bytes // or if the length increment would overflow for zero-sized types. - if self.len == self.buf.cap() { + if self.buf.len == self.buf.cap() { self.buf.grow_one(); } unsafe { - let end = self.buf.ptr().add(self.len); + let end = self.buf.ptr().add(self.buf.len); ptr::write(end, value); - self.len += 1; + self.buf.len += 1; } } @@ -1592,11 +1592,11 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// ``` #[inline] pub fn pop(&mut self) -> Option { - if self.len == 0 { + if self.buf.len == 0 { None } else { unsafe { - self.len -= 1; + self.buf.len -= 1; Some(ptr::read(self.as_ptr().add(self.len()))) } } @@ -1636,7 +1636,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { self.reserve(count); let len = self.len(); ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count); - self.len += count; + self.buf.len += count; } /// Creates a draining iterator that removes the specified range in the vector @@ -1753,7 +1753,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { /// ``` #[inline] pub fn len(&self) -> usize { - self.len + self.buf.len } /// Returns `true` if the vector contains no elements. @@ -1802,7 +1802,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { pub fn split_off(&mut self, at: usize) -> Self { assert!(at <= self.len(), "`at` out of bounds"); - let other_len = self.len - at; + let other_len = self.buf.len - at; let mut other = Vec::with_capacity_in(other_len, self.buf.bump()); // Unsafely `set_len` and copy items to `other`. @@ -2069,7 +2069,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> { // Use SetLenOnDrop to work around bug where compiler // may not realize the store through `ptr` through self.set_len() // don't alias. - let mut local_len = SetLenOnDrop::new(&mut self.len); + let mut local_len = SetLenOnDrop::new(&mut self.buf.len); // Write all elements except the last one for _ in 1..n { @@ -2209,7 +2209,7 @@ impl<'bump, T: 'bump> ops::Deref for Vec<'bump, T> { unsafe { let p = self.buf.ptr(); // assume(!p.is_null()); - slice::from_raw_parts(p, self.len) + slice::from_raw_parts(p, self.buf.len) } } } @@ -2219,7 +2219,7 @@ impl<'bump, T: 'bump> ops::DerefMut for Vec<'bump, T> { unsafe { let ptr = self.buf.ptr(); // assume(!ptr.is_null()); - slice::from_raw_parts_mut(ptr, self.len) + slice::from_raw_parts_mut(ptr, self.buf.len) } } } @@ -2802,7 +2802,7 @@ impl<'a, 'bump, T> Drain<'a, 'bump, T> { /// Return whether we filled the entire range. (`replace_with.next()` didn’t return `None`.) unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { let vec = self.vec.as_mut(); - let range_start = vec.len; + let range_start = vec.buf.len; let range_end = self.tail_start; let range_slice = slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start); @@ -2810,7 +2810,7 @@ impl<'a, 'bump, T> Drain<'a, 'bump, T> { for place in range_slice { if let Some(new_item) = replace_with.next() { ptr::write(place, new_item); - vec.len += 1; + vec.buf.len += 1; } else { return false; } diff --git a/crates/oxc_allocator/src/vec2/raw_vec.rs b/crates/oxc_allocator/src/vec2/raw_vec.rs index 871c8e5bd6660..c0006660aad6a 100644 --- a/crates/oxc_allocator/src/vec2/raw_vec.rs +++ b/crates/oxc_allocator/src/vec2/raw_vec.rs @@ -63,10 +63,12 @@ use bumpalo::collections::CollectionAllocErr::{self, AllocErr, CapacityOverflow} /// field. This allows zero-sized types to not be special-cased by consumers of /// this type. #[allow(missing_debug_implementations)] +#[repr(C)] pub struct RawVec<'a, T> { ptr: NonNull, - cap: usize, a: &'a Bump, + cap: usize, + pub(super) len: usize, } impl<'a, T> RawVec<'a, T> { @@ -74,7 +76,7 @@ impl<'a, T> RawVec<'a, T> { /// the returned RawVec. pub fn new_in(a: &'a Bump) -> Self { // `cap: 0` means "unallocated". zero-sized types are ignored. - RawVec { ptr: NonNull::dangling(), cap: 0, a } + RawVec { ptr: NonNull::dangling(), a, cap: 0, len: 0 } } /// Like `with_capacity` but parameterized over the choice of @@ -111,7 +113,7 @@ impl<'a, T> RawVec<'a, T> { } }; - RawVec { ptr, cap, a } + RawVec { ptr, a, cap, len: 0 } } } } @@ -124,8 +126,8 @@ impl<'a, T> RawVec<'a, T> { /// The ptr must be allocated (via the given allocator `a`), and with the given capacity. The /// capacity cannot exceed `isize::MAX` (only a concern on 32-bit systems). /// If the ptr and capacity come from a RawVec created via `a`, then this is guaranteed. - pub unsafe fn from_raw_parts_in(ptr: *mut T, cap: usize, a: &'a Bump) -> Self { - RawVec { ptr: NonNull::new_unchecked(ptr), cap, a } + pub unsafe fn from_raw_parts_in(ptr: *mut T, a: &'a Bump, cap: usize, len: usize) -> Self { + RawVec { ptr: NonNull::new_unchecked(ptr), a, cap, len } } } diff --git a/tasks/ast_tools/src/generators/raw_transfer.rs b/tasks/ast_tools/src/generators/raw_transfer.rs index 75975be5221f4..118738d822d3e 100644 --- a/tasks/ast_tools/src/generators/raw_transfer.rs +++ b/tasks/ast_tools/src/generators/raw_transfer.rs @@ -25,8 +25,7 @@ use crate::{ use super::define_generator; // Offsets of `Vec`'s fields. -// These are based on observation, and are not stable. -// They will be fully reliable once we implement our own `Vec` type and make it `#[repr(C)]`. +// `Vec` is `#[repr(transparent)]` and `RawVec` is `#[repr(C)]`, so these offsets are fixed. const VEC_PTR_FIELD_OFFSET: usize = 0; const VEC_LEN_FIELD_OFFSET: usize = 24;