Skip to content

Commit

Permalink
feat(allocator): introduce Vec::from_array_in (#7331)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
overlookmotel committed Nov 18, 2024
1 parent 1938a1d commit 39afb48
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 7 deletions.
7 changes: 7 additions & 0 deletions crates/oxc_allocator/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ impl<'alloc, T: ?Sized> Box<'alloc, T> {
pub(crate) const unsafe fn from_non_null(ptr: NonNull<T>) -> 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<T> {
boxed.0
}
}

impl<'alloc, T: ?Sized> ops::Deref for Box<'alloc, T> {
Expand Down
28 changes: 28 additions & 0 deletions crates/oxc_allocator/src/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<const N: usize>(array: [T; N], allocator: &'alloc Allocator) -> Self {
let boxed = Box::new_in(array, allocator);
let ptr = Box::into_non_null(boxed).as_ptr().cast::<T>();
// 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.
Expand Down
10 changes: 10 additions & 0 deletions crates/oxc_ast/src/ast_builder_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, const N: usize>(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]
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_isolated_declarations/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
]),
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_isolated_declarations/src/return_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 2 additions & 4 deletions crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down

0 comments on commit 39afb48

Please sign in to comment.