From edff7130c849496c26e02c12cb135e390542c4b8 Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Wed, 16 Jul 2025 22:32:48 +0100 Subject: [PATCH] refactor(allocator): store allocation pointer in metadata --- .../src/generated/assert_layouts.rs | 32 +++++++++-------- crates/oxc_allocator/src/pool_fixed_size.rs | 34 ++++++++++++++----- .../oxc_ast_macros/src/generated/structs.rs | 4 +-- napi/parser/generated/constants.js | 2 +- napi/parser/generated/deserialize/js.js | 18 +++++----- napi/parser/generated/deserialize/ts.js | 18 +++++----- napi/parser/generated/lazy/constructors.js | 20 +++++------ napi/parser/src/generated/assert_layouts.rs | 32 +++++++++-------- napi/parser/src/raw_transfer_types.rs | 20 ++++++++--- 9 files changed, 106 insertions(+), 74 deletions(-) diff --git a/crates/oxc_allocator/src/generated/assert_layouts.rs b/crates/oxc_allocator/src/generated/assert_layouts.rs index 4ab3d403335c5..b5e37596946b7 100644 --- a/crates/oxc_allocator/src/generated/assert_layouts.rs +++ b/crates/oxc_allocator/src/generated/assert_layouts.rs @@ -9,26 +9,28 @@ use crate::*; #[cfg(target_pointer_width = "64")] const _: () = { - // Padding: 2 bytes - assert!(size_of::() == 16); - assert!(align_of::() == 4); - assert!(offset_of!(RawTransferMetadata2, data_offset) == 0); - assert!(offset_of!(RawTransferMetadata2, is_ts) == 12); - assert!(offset_of!(RawTransferMetadata2, id) == 4); - assert!(offset_of!(RawTransferMetadata2, can_be_freed) == 13); - assert!(offset_of!(RawTransferMetadata2, _padding) == 8); + // Padding: 6 bytes + assert!(size_of::() == 32); + assert!(align_of::() == 8); + assert!(offset_of!(RawTransferMetadata2, data_offset) == 16); + assert!(offset_of!(RawTransferMetadata2, is_ts) == 24); + assert!(offset_of!(RawTransferMetadata2, id) == 20); + assert!(offset_of!(RawTransferMetadata2, can_be_freed) == 25); + assert!(offset_of!(RawTransferMetadata2, alloc_ptr) == 8); + assert!(offset_of!(RawTransferMetadata2, _padding) == 0); }; #[cfg(target_pointer_width = "32")] const _: () = { // Padding: 2 bytes - assert!(size_of::() == 16); - assert!(align_of::() == 4); - assert!(offset_of!(RawTransferMetadata2, data_offset) == 0); - assert!(offset_of!(RawTransferMetadata2, is_ts) == 12); - assert!(offset_of!(RawTransferMetadata2, id) == 4); - assert!(offset_of!(RawTransferMetadata2, can_be_freed) == 13); - assert!(offset_of!(RawTransferMetadata2, _padding) == 8); + assert!(size_of::() == 24); + assert!(align_of::() == 8); + assert!(offset_of!(RawTransferMetadata2, data_offset) == 12); + assert!(offset_of!(RawTransferMetadata2, is_ts) == 20); + assert!(offset_of!(RawTransferMetadata2, id) == 16); + assert!(offset_of!(RawTransferMetadata2, can_be_freed) == 21); + assert!(offset_of!(RawTransferMetadata2, alloc_ptr) == 8); + assert!(offset_of!(RawTransferMetadata2, _padding) == 0); }; #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))] diff --git a/crates/oxc_allocator/src/pool_fixed_size.rs b/crates/oxc_allocator/src/pool_fixed_size.rs index f86079b054bac..60e9ba64dde88 100644 --- a/crates/oxc_allocator/src/pool_fixed_size.rs +++ b/crates/oxc_allocator/src/pool_fixed_size.rs @@ -125,16 +125,25 @@ pub struct RawTransferMetadata2 { /// * Also be set to `true` if `FixedSizeAllocator` is dropped on Rust side. /// Memory will be freed in finalizer when JS garbage collector collects the buffer. pub(crate) can_be_freed: AtomicBool, - /// Padding to pad struct to size 16. - pub(crate) _padding: u32, + /// Pointer to start of original allocation backing the `Allocator`. + pub(crate) alloc_ptr: NonNull, + /// Padding to pad struct to size 32. + pub(crate) _padding: u64, } use RawTransferMetadata2 as RawTransferMetadata; const METADATA_SIZE: usize = size_of::(); impl RawTransferMetadata { - fn new(id: u32) -> Self { - Self { data_offset: 0, is_ts: false, id, can_be_freed: AtomicBool::new(true), _padding: 0 } + fn new(id: u32, alloc_ptr: NonNull) -> Self { + Self { + data_offset: 0, + is_ts: false, + id, + can_be_freed: AtomicBool::new(true), + alloc_ptr, + _padding: 0, + } } } @@ -175,8 +184,6 @@ const ALLOC_LAYOUT: Layout = match Layout::from_size_align(ALLOC_SIZE, ALLOC_ALI pub struct FixedSizeAllocator { /// `Allocator` which utilizes part of the original allocation allocator: ManuallyDrop, - /// Pointer to start of original allocation - alloc_ptr: NonNull, } impl FixedSizeAllocator { @@ -215,14 +222,14 @@ impl FixedSizeAllocator { let allocator = unsafe { Allocator::from_raw_parts(chunk_ptr, CHUNK_SIZE) }; // Store metadata after allocator chunk - let metadata = RawTransferMetadata::new(id); + let metadata = RawTransferMetadata::new(id, alloc_ptr); // SAFETY: `chunk_ptr` is at least `BUFFER_SIZE` bytes from the end of the allocation. // `CHUNK_SIZE` had the size of `RawTransferMetadata` subtracted from it. // So there is space within the allocation for `RawTransferMetadata` after the `Allocator`'s chunk. unsafe { chunk_ptr.add(CHUNK_SIZE).cast::().write(metadata) }; // Store pointer to original allocation, so it can be used to deallocate in `drop` - Self { allocator: ManuallyDrop::new(allocator), alloc_ptr } + Self { allocator: ManuallyDrop::new(allocator) } } /// Reset this [`FixedSizeAllocator`]. @@ -245,8 +252,17 @@ impl FixedSizeAllocator { impl Drop for FixedSizeAllocator { fn drop(&mut self) { + // Get pointer to start of allocation backing this `FixedSizeAllocator` + let alloc_ptr = { + let metadata_ptr = self.allocator.end_ptr().cast::(); + // SAFETY: `FixedSizeAllocator` is being dropped, so no other references to data in it may exist. + // `FixedSizeAllocator::new` wrote `RawTransferMetadata` to the location pointed to by `end_ptr`. + let metadata = unsafe { metadata_ptr.as_ref() }; + metadata.alloc_ptr + }; + // SAFETY: Originally allocated from `System` allocator at `alloc_ptr`, with layout `ALLOC_LAYOUT` - unsafe { System.dealloc(self.alloc_ptr.as_ptr(), ALLOC_LAYOUT) } + unsafe { System.dealloc(alloc_ptr.as_ptr(), ALLOC_LAYOUT) } } } diff --git a/crates/oxc_ast_macros/src/generated/structs.rs b/crates/oxc_ast_macros/src/generated/structs.rs index df0efd19235b7..4e89bdb028f95 100644 --- a/crates/oxc_ast_macros/src/generated/structs.rs +++ b/crates/oxc_ast_macros/src/generated/structs.rs @@ -135,7 +135,7 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map { ("Decorator", StructDetails { field_order: None }), ("CharacterClass", StructDetails { field_order: Some(&[0, 2, 3, 4, 1]) }), ("TemplateElementValue", StructDetails { field_order: None }), - ("RawTransferMetadata", StructDetails { field_order: Some(&[0, 3, 1, 4, 2]) }), + ("RawTransferMetadata", StructDetails { field_order: Some(&[2, 4, 3, 5, 1, 0]) }), ("TSTypeParameter", StructDetails { field_order: None }), ("SourceType", StructDetails { field_order: None }), ("ErrorLabel", StructDetails { field_order: Some(&[1, 0]) }), @@ -266,7 +266,7 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map { ("ImportAttribute", StructDetails { field_order: None }), ("TSConditionalType", StructDetails { field_order: None }), ("TSNamespaceExportDeclaration", StructDetails { field_order: None }), - ("RawTransferMetadata2", StructDetails { field_order: Some(&[0, 3, 1, 4, 2]) }), + ("RawTransferMetadata2", StructDetails { field_order: Some(&[2, 4, 3, 5, 1, 0]) }), ("AssignmentTargetWithDefault", StructDetails { field_order: None }), ("RegExpLiteral", StructDetails { field_order: None }), ("CapturingGroup", StructDetails { field_order: None }), diff --git a/napi/parser/generated/constants.js b/napi/parser/generated/constants.js index 501d9c0e4ef1d..b68321f9ce1ac 100644 --- a/napi/parser/generated/constants.js +++ b/napi/parser/generated/constants.js @@ -4,7 +4,7 @@ const BUFFER_SIZE = 2147483632, BUFFER_ALIGN = 4294967296, DATA_POINTER_POS_32 = 536870904, - IS_TS_FLAG_POS = 2147483628, + IS_TS_FLAG_POS = 2147483624, PROGRAM_OFFSET = 0; module.exports = { diff --git a/napi/parser/generated/deserialize/js.js b/napi/parser/generated/deserialize/js.js index f4f7da8f4a9a2..173fcf6163e51 100644 --- a/napi/parser/generated/deserialize/js.js +++ b/napi/parser/generated/deserialize/js.js @@ -4028,6 +4028,15 @@ function deserializeBool(pos) { return uint8[pos] === 1; } +function deserializeU8(pos) { + return uint8[pos]; +} + +function deserializeU64(pos) { + const pos32 = pos >> 2; + return uint32[pos32] + uint32[pos32 + 1] * 4294967296; +} + function deserializeStr(pos) { const pos32 = pos >> 2, len = uint32[pos32 + 2]; @@ -4859,10 +4868,6 @@ function deserializeF64(pos) { return float64[pos >> 3]; } -function deserializeU8(pos) { - return uint8[pos]; -} - function deserializeBoxJSXOpeningElement(pos) { return deserializeJSXOpeningElement(uint32[pos >> 2]); } @@ -5243,11 +5248,6 @@ function deserializeOptionNameSpan(pos) { return deserializeNameSpan(pos); } -function deserializeU64(pos) { - const pos32 = pos >> 2; - return uint32[pos32] + uint32[pos32 + 1] * 4294967296; -} - function deserializeOptionU64(pos) { if (uint8[pos] === 0) return null; return deserializeU64(pos + 8); diff --git a/napi/parser/generated/deserialize/ts.js b/napi/parser/generated/deserialize/ts.js index f415566fb9518..f4da67c282284 100644 --- a/napi/parser/generated/deserialize/ts.js +++ b/napi/parser/generated/deserialize/ts.js @@ -4159,6 +4159,15 @@ function deserializeBool(pos) { return uint8[pos] === 1; } +function deserializeU8(pos) { + return uint8[pos]; +} + +function deserializeU64(pos) { + const pos32 = pos >> 2; + return uint32[pos32] + uint32[pos32 + 1] * 4294967296; +} + function deserializeStr(pos) { const pos32 = pos >> 2, len = uint32[pos32 + 2]; @@ -4990,10 +4999,6 @@ function deserializeF64(pos) { return float64[pos >> 3]; } -function deserializeU8(pos) { - return uint8[pos]; -} - function deserializeBoxJSXOpeningElement(pos) { return deserializeJSXOpeningElement(uint32[pos >> 2]); } @@ -5374,11 +5379,6 @@ function deserializeOptionNameSpan(pos) { return deserializeNameSpan(pos); } -function deserializeU64(pos) { - const pos32 = pos >> 2; - return uint32[pos32] + uint32[pos32 + 1] * 4294967296; -} - function deserializeOptionU64(pos) { if (uint8[pos] === 0) return null; return deserializeU64(pos + 8); diff --git a/napi/parser/generated/lazy/constructors.js b/napi/parser/generated/lazy/constructors.js index ce34335c37aff..07d13ed378c2d 100644 --- a/napi/parser/generated/lazy/constructors.js +++ b/napi/parser/generated/lazy/constructors.js @@ -12480,6 +12480,16 @@ function constructBool(pos, ast) { return ast.buffer[pos] === 1; } +function constructU8(pos, ast) { + return ast.buffer[pos]; +} + +function constructU64(pos, ast) { + const { uint32 } = ast.buffer, + pos32 = pos >> 2; + return uint32[pos32] + uint32[pos32 + 1] * 4294967296; +} + function constructStr(pos, ast) { const pos32 = pos >> 2, { buffer } = ast, @@ -13357,10 +13367,6 @@ function constructF64(pos, ast) { return ast.buffer.float64[pos >> 3]; } -function constructU8(pos, ast) { - return ast.buffer[pos]; -} - function constructBoxJSXOpeningElement(pos, ast) { return new JSXOpeningElement(ast.buffer.uint32[pos >> 2], ast); } @@ -13757,12 +13763,6 @@ function constructOptionNameSpan(pos, ast) { return new NameSpan(pos, ast); } -function constructU64(pos, ast) { - const { uint32 } = ast.buffer, - pos32 = pos >> 2; - return uint32[pos32] + uint32[pos32 + 1] * 4294967296; -} - function constructOptionU64(pos, ast) { if (ast.buffer[pos] === 0) return null; return constructU64(pos + 8, ast); diff --git a/napi/parser/src/generated/assert_layouts.rs b/napi/parser/src/generated/assert_layouts.rs index feb784164edcf..8a28a4b13990d 100644 --- a/napi/parser/src/generated/assert_layouts.rs +++ b/napi/parser/src/generated/assert_layouts.rs @@ -17,14 +17,15 @@ const _: () = { assert!(offset_of!(RawTransferData, module) == 152); assert!(offset_of!(RawTransferData, errors) == 256); - // Padding: 2 bytes - assert!(size_of::() == 16); - assert!(align_of::() == 4); - assert!(offset_of!(RawTransferMetadata, data_offset) == 0); - assert!(offset_of!(RawTransferMetadata, is_ts) == 12); - assert!(offset_of!(RawTransferMetadata, id) == 4); - assert!(offset_of!(RawTransferMetadata, can_be_freed) == 13); - assert!(offset_of!(RawTransferMetadata, _padding) == 8); + // Padding: 6 bytes + assert!(size_of::() == 32); + assert!(align_of::() == 8); + assert!(offset_of!(RawTransferMetadata, data_offset) == 16); + assert!(offset_of!(RawTransferMetadata, is_ts) == 24); + assert!(offset_of!(RawTransferMetadata, id) == 20); + assert!(offset_of!(RawTransferMetadata, can_be_freed) == 25); + assert!(offset_of!(RawTransferMetadata, alloc_ptr) == 8); + assert!(offset_of!(RawTransferMetadata, _padding) == 0); // Padding: 7 bytes assert!(size_of::() == 80); @@ -78,13 +79,14 @@ const _: () = { assert!(offset_of!(RawTransferData, errors) == 172); // Padding: 2 bytes - assert!(size_of::() == 16); - assert!(align_of::() == 4); - assert!(offset_of!(RawTransferMetadata, data_offset) == 0); - assert!(offset_of!(RawTransferMetadata, is_ts) == 12); - assert!(offset_of!(RawTransferMetadata, id) == 4); - assert!(offset_of!(RawTransferMetadata, can_be_freed) == 13); - assert!(offset_of!(RawTransferMetadata, _padding) == 8); + assert!(size_of::() == 24); + assert!(align_of::() == 8); + assert!(offset_of!(RawTransferMetadata, data_offset) == 12); + assert!(offset_of!(RawTransferMetadata, is_ts) == 20); + assert!(offset_of!(RawTransferMetadata, id) == 16); + assert!(offset_of!(RawTransferMetadata, can_be_freed) == 21); + assert!(offset_of!(RawTransferMetadata, alloc_ptr) == 8); + assert!(offset_of!(RawTransferMetadata, _padding) == 0); // Padding: 3 bytes assert!(size_of::() == 44); diff --git a/napi/parser/src/raw_transfer_types.rs b/napi/parser/src/raw_transfer_types.rs index 0182dc2a5687f..c33142bda9446 100644 --- a/napi/parser/src/raw_transfer_types.rs +++ b/napi/parser/src/raw_transfer_types.rs @@ -1,6 +1,9 @@ #![cfg_attr(not(all(target_pointer_width = "64", target_endian = "little")), expect(dead_code))] -use std::sync::{Arc, atomic::AtomicBool}; +use std::{ + ptr::NonNull, + sync::{Arc, atomic::AtomicBool}, +}; use rustc_hash::FxHashMap; @@ -40,13 +43,22 @@ pub struct RawTransferMetadata { pub(crate) id: u32, /// Not used in this implementation. Only used by `oxc_allocator`. pub(crate) can_be_freed: AtomicBool, - /// Padding to pad struct to size 16. - pub(crate) _padding: u32, + /// Not used in this implementation. Only used by `oxc_allocator`. + pub(crate) alloc_ptr: NonNull, + /// Padding to pad struct to size 32. + pub(crate) _padding: u64, } impl RawTransferMetadata { pub fn new(data_offset: u32, is_ts: bool) -> Self { - Self { data_offset, is_ts, id: 0, can_be_freed: AtomicBool::new(false), _padding: 0 } + Self { + data_offset, + is_ts, + id: 0, + can_be_freed: AtomicBool::new(false), + alloc_ptr: NonNull::dangling(), + _padding: 0, + } } }