From 39afb48025a071faebc0d324079e9f8ba60b61ba Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Mon, 18 Nov 2024 02:35:46 +0000 Subject: [PATCH] feat(allocator): introduce `Vec::from_array_in` (#7331) Because we lack specialization in stable Rust, `Vec::from_iter_in` is unable to take advantage of the fact that `[T; N]` has a statically knowable size. Introduce `Vec::from_array_in` for this case, which should be able to create the `Vec` with a single static-sized memcpy, or may allow the compiler to see that it can construct the array directly in the arena, rather than construct on stack and then copy to the arena. Also add a corresponding `AstBuilder::vec_from_array` method, and use it in various places in codebase. --- crates/oxc_allocator/src/boxed.rs | 7 +++++ crates/oxc_allocator/src/vec.rs | 28 +++++++++++++++++++ crates/oxc_ast/src/ast_builder_impl.rs | 10 +++++++ .../oxc_isolated_declarations/src/function.rs | 2 +- .../src/return_type.rs | 2 +- .../src/ast_passes/peephole_fold_constants.rs | 6 ++-- .../src/es2016/exponentiation_operator.rs | 2 +- 7 files changed, 50 insertions(+), 7 deletions(-) diff --git a/crates/oxc_allocator/src/boxed.rs b/crates/oxc_allocator/src/boxed.rs index 3271631cb..a91e7337f 100644 --- a/crates/oxc_allocator/src/boxed.rs +++ b/crates/oxc_allocator/src/boxed.rs @@ -99,6 +99,13 @@ impl<'alloc, T: ?Sized> Box<'alloc, T> { pub(crate) const unsafe fn from_non_null(ptr: NonNull) -> Self { Self(ptr, PhantomData) } + + /// Consume a [`Box`] and return a [`NonNull`] pointer to its contents. + #[inline] + #[expect(clippy::needless_pass_by_value)] + pub fn into_non_null(boxed: Self) -> NonNull { + boxed.0 + } } impl<'alloc, T: ?Sized> ops::Deref for Box<'alloc, T> { diff --git a/crates/oxc_allocator/src/vec.rs b/crates/oxc_allocator/src/vec.rs index 471e47e47..057333aae 100644 --- a/crates/oxc_allocator/src/vec.rs +++ b/crates/oxc_allocator/src/vec.rs @@ -117,6 +117,34 @@ impl<'alloc, T> Vec<'alloc, T> { Self(vec) } + /// Create a new [`Vec`] from a fixed-size array, allocated in the given `allocator`. + /// + /// This is preferable to `from_iter_in` where source is an array, as size is statically known, + /// and compiler is more likely to construct the values directly in arena, rather than constructing + /// on stack and then copying to arena. + /// + /// # Examples + /// + /// ``` + /// use oxc_allocator::{Allocator, Vec}; + /// + /// let allocator = Allocator::default(); + /// + /// let array: [u32; 4] = [1, 2, 3, 4]; + /// let vec = Vec::from_array_in(array, &allocator); + /// ``` + #[inline] + pub fn from_array_in(array: [T; N], allocator: &'alloc Allocator) -> Self { + let boxed = Box::new_in(array, allocator); + let ptr = Box::into_non_null(boxed).as_ptr().cast::(); + // SAFETY: `ptr` has correct alignment - it was just allocated as `[T; N]`. + // `ptr` was allocated with correct size for `[T; N]`. + // `len` and `capacity` are both `N`. + // Allocated size cannot be larger than `isize::MAX`, or `Box::new_in` would have failed. + let vec = unsafe { vec::Vec::from_raw_parts_in(ptr, N, N, &**allocator) }; + Self(ManuallyDrop::new(vec)) + } + /// Converts the vector into [`Box<[T]>`][owned slice]. /// /// Any excess capacity the vector has will not be included in the slice. diff --git a/crates/oxc_ast/src/ast_builder_impl.rs b/crates/oxc_ast/src/ast_builder_impl.rs index 9c5c4ccea..80937d833 100644 --- a/crates/oxc_ast/src/ast_builder_impl.rs +++ b/crates/oxc_ast/src/ast_builder_impl.rs @@ -67,6 +67,16 @@ impl<'a> AstBuilder<'a> { Vec::from_iter_in(iter, self.allocator) } + /// Create [`Vec`] from a fixed-size array. + /// + /// This is preferable to `vec_from_iter` where source is an array, as size is statically known, + /// and compiler is more likely to construct the values directly in arena, rather than constructing + /// on stack and then copying to arena. + #[inline] + pub fn vec_from_array(self, array: [T; N]) -> Vec<'a, T> { + Vec::from_array_in(array, self.allocator) + } + /// Move a string slice into the memory arena, returning a reference to the slice /// in the heap. #[inline] diff --git a/crates/oxc_isolated_declarations/src/function.rs b/crates/oxc_isolated_declarations/src/function.rs index bfcf00bec..380098817 100644 --- a/crates/oxc_isolated_declarations/src/function.rs +++ b/crates/oxc_isolated_declarations/src/function.rs @@ -99,7 +99,7 @@ impl<'a> IsolatedDeclarations<'a> { SPAN, self.ast.ts_type_union_type( SPAN, - self.ast.vec_from_iter([ + self.ast.vec_from_array([ ts_type, self.ast.ts_type_undefined_keyword(SPAN), ]), diff --git a/crates/oxc_isolated_declarations/src/return_type.rs b/crates/oxc_isolated_declarations/src/return_type.rs index e785245fb..55a6ca515 100644 --- a/crates/oxc_isolated_declarations/src/return_type.rs +++ b/crates/oxc_isolated_declarations/src/return_type.rs @@ -113,7 +113,7 @@ impl<'a> FunctionReturnType<'a> { let types = transformer .ast - .vec_from_iter([expr_type, transformer.ast.ts_type_undefined_keyword(SPAN)]); + .vec_from_array([expr_type, transformer.ast.ts_type_undefined_keyword(SPAN)]); expr_type = transformer.ast.ts_type_union_type(SPAN, types); } Some(expr_type) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 282ddbe10..2a547f25d 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -156,9 +156,7 @@ impl<'a, 'b> PeepholeFoldConstants { // or: false_with_sideeffects && foo() => false_with_sideeffects, foo() let left = ctx.ast.move_expression(&mut logical_expr.left); let right = ctx.ast.move_expression(&mut logical_expr.right); - let mut vec = ctx.ast.vec_with_capacity(2); - vec.push(left); - vec.push(right); + let vec = ctx.ast.vec_from_array([left, right]); let sequence_expr = ctx.ast.expression_sequence(logical_expr.span, vec); return Some(sequence_expr); } else if let Expression::LogicalExpression(left_child) = &mut logical_expr.left { @@ -201,7 +199,7 @@ impl<'a, 'b> PeepholeFoldConstants { ValueType::Null | ValueType::Undefined => { Some(if left.may_have_side_effects() { // e.g. `(a(), null) ?? 1` => `(a(), null, 1)` - let expressions = ctx.ast.vec_from_iter([ + let expressions = ctx.ast.vec_from_array([ ctx.ast.move_expression(&mut logical_expr.left), ctx.ast.move_expression(&mut logical_expr.right), ]); diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index 38254c188..ea9c73caa 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -545,7 +545,7 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { let property = ctx.ast.identifier_name(SPAN, "pow"); let callee = Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false)); - let arguments = ctx.ast.vec_from_iter([Argument::from(left), Argument::from(right)]); + let arguments = ctx.ast.vec_from_array([Argument::from(left), Argument::from(right)]); ctx.ast.expression_call(SPAN, callee, NONE, arguments, false) }