Add fast path for cloning by iterating fast holey arrays.

This fast path copies the backing store and replaces holes with undefined.
In the case where the array is holey but there is no actual holes, the
resulting array is of the same elements kind as the source array. If a hole
does appear, the resulting array will be of PACKED_ELEMENTS kind so that it
can contain undefined.

The builtin CloneFastJSArrayFillingHoles includes this fast path, but
CloneFastJSArray does not (it still behaves as before). In case of fast
packed arrays, CloneFastJSArrayFillingHoles behaves the same as
CloneFastJSArray.

Bug: chromium:881273, v8:7980
Change-Id: I49c641c1a673313f06aeed93077031ab6b017b6d
Reviewed-on: https://chromium-review.googlesource.com/1236573
Commit-Queue: Hai Dang <dhai@google.com>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56209}
This commit is contained in:
Hai Dang 2018-09-25 14:55:17 +02:00 committed by Commit Bot
parent 790b687c65
commit 9f7ec7d479
5 changed files with 296 additions and 49 deletions

View File

@ -1694,6 +1694,27 @@ TF_BUILTIN(CloneFastJSArray, ArrayBuiltinsAssembler) {
Return(CloneFastJSArray(context, array, mode));
}
// This builtin copies the backing store of fast arrays, while converting any
// holes to undefined.
// - If there are no holes in the source, its ElementsKind will be preserved. In
// that case, this builtin should perform as fast as CloneFastJSArray. (In fact,
// for fast packed arrays, the behavior is equivalent to CloneFastJSArray.)
// - If there are holes in the source, the ElementsKind of the "copy" will be
// PACKED_ELEMENTS (such that undefined can be stored).
TF_BUILTIN(CloneFastJSArrayFillingHoles, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
CSA_ASSERT(this,
Word32Or(Word32BinaryNot(IsHoleyFastElementsKind(
LoadMapElementsKind(LoadMap(array)))),
Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
ParameterMode mode = OptimalParameterMode();
Return(CloneFastJSArray(context, array, mode, nullptr,
HoleConversionMode::kConvertToUndefined));
}
TF_BUILTIN(ArrayFindLoopContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));

View File

@ -326,6 +326,7 @@ namespace internal {
CPP(ArrayUnshift) \
/* Support for Array.from and other array-copying idioms */ \
TFS(CloneFastJSArray, kSource) \
TFS(CloneFastJSArrayFillingHoles, kSource) \
TFS(ExtractFastJSArray, kSource, kBegin, kCount) \
/* ES6 #sec-array.prototype.every */ \
TFS(ArrayEveryLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \

View File

@ -260,8 +260,10 @@ TF_BUILTIN(IterableToListMayPreserveHoles, IteratorBuiltinsAssembler) {
TailCallBuiltin(Builtins::kIterableToList, context, iterable, iterator_fn);
}
// This builtin uses the default Symbol.iterator for the iterator, and takes
// the fast path only if the iterable is a fast _packed_ array.
// This builtin loads the property Symbol.iterator as the iterator, and has a
// fast path for fast arrays. In case of fast holey arrays, holes will be
// converted to undefined to reflect iteration semantics. Note that replacement
// by undefined is only correct when the NoElements protector is valid.
TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> iterable = CAST(Parameter(Descriptor::kIterable));
@ -271,13 +273,7 @@ TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
GotoIfNot(IsFastJSArrayWithNoCustomIteration(iterable, context), &slow_path);
// Here we are guaranteed that iterable is a fast JSArray with an original
// iterator.
Node* elements_kind = LoadMapElementsKind(LoadMap(CAST(iterable)));
// Take the slow path if the array is holey.
GotoIf(IsHoleyFastElementsKind(elements_kind), &slow_path);
// This is a fast-path for ignoring the iterator. Here we are guaranteed that
// {iterable} is a fast _packed_ JSArray.
TailCallBuiltin(Builtins::kCloneFastJSArray, context, iterable);
TailCallBuiltin(Builtins::kCloneFastJSArrayFillingHoles, context, iterable);
BIND(&slow_path);
{

View File

@ -3956,27 +3956,66 @@ Node* CodeStubAssembler::ExtractFastJSArray(Node* context, Node* array,
Node* CodeStubAssembler::CloneFastJSArray(Node* context, Node* array,
ParameterMode mode,
Node* allocation_site) {
Node* allocation_site,
HoleConversionMode convert_holes) {
// TODO(dhai): we should be able to assert IsFastJSArray(array) here, but this
// function is also used to copy boilerplates even when the no-elements
// protector is invalid. This function should be renamed to reflect its uses.
CSA_ASSERT(this, IsJSArray(array));
Node* original_array_map = LoadMap(array);
Node* elements_kind = LoadMapElementsKind(original_array_map);
Node* length = LoadJSArrayLength(array);
Node* new_elements = ExtractFixedArray(
Node* new_elements = nullptr;
VARIABLE(var_new_elements, MachineRepresentation::kTagged);
TVARIABLE(Int32T, var_elements_kind, LoadMapElementsKind(LoadMap(array)));
Label allocate_jsarray(this), holey_extract(this);
bool need_conversion =
convert_holes == HoleConversionMode::kConvertToUndefined;
if (need_conversion) {
// We need to take care of holes, if the array is of holey elements kind.
GotoIf(IsHoleyFastElementsKind(var_elements_kind.value()), &holey_extract);
}
// Simple extraction that preserves holes.
new_elements = ExtractFixedArray(
LoadElements(array), IntPtrOrSmiConstant(0, mode),
TaggedToParameter(length, mode), nullptr,
ExtractFixedArrayFlag::kAllFixedArraysDontCopyCOW, mode);
var_new_elements.Bind(new_elements);
Goto(&allocate_jsarray);
// Use the cannonical map for the Array's ElementsKind
if (need_conversion) {
BIND(&holey_extract);
// Convert holes to undefined.
TVARIABLE(BoolT, var_holes_converted, Int32FalseConstant());
// Copy |array|'s elements store. The copy will be compatible with the
// original elements kind unless there are holes in the source. Any holes
// get converted to undefined, hence in that case the copy is compatible
// only with PACKED_ELEMENTS and HOLEY_ELEMENTS, and we will choose
// PACKED_ELEMENTS. Also, if we want to replace holes, we must not use
// ExtractFixedArrayFlag::kDontCopyCOW.
new_elements = ExtractFixedArray(
LoadElements(array), IntPtrOrSmiConstant(0, mode),
TaggedToParameter(length, mode), nullptr,
ExtractFixedArrayFlag::kAllFixedArrays, mode, &var_holes_converted);
var_new_elements.Bind(new_elements);
// If the array type didn't change, use the original elements kind.
GotoIfNot(var_holes_converted.value(), &allocate_jsarray);
// Otherwise use PACKED_ELEMENTS for the target's elements kind.
var_elements_kind = Int32Constant(PACKED_ELEMENTS);
Goto(&allocate_jsarray);
}
BIND(&allocate_jsarray);
// Use the cannonical map for the chosen elements kind.
Node* native_context = LoadNativeContext(context);
Node* array_map = LoadJSArrayElementsMap(elements_kind, native_context);
Node* array_map =
LoadJSArrayElementsMap(var_elements_kind.value(), native_context);
Node* result = AllocateUninitializedJSArrayWithoutElements(array_map, length,
allocation_site);
StoreObjectField(result, JSObject::kElementsOffset, new_elements);
StoreObjectField(result, JSObject::kElementsOffset, var_new_elements.value());
return result;
}
@ -4019,7 +4058,8 @@ TNode<FixedArrayBase> CodeStubAssembler::AllocateFixedArray(
TNode<FixedArray> CodeStubAssembler::ExtractToFixedArray(
Node* source, Node* first, Node* count, Node* capacity, Node* source_map,
ElementsKind from_kind, AllocationFlags allocation_flags,
ExtractFixedArrayFlags extract_flags, ParameterMode parameter_mode) {
ExtractFixedArrayFlags extract_flags, ParameterMode parameter_mode,
HoleConversionMode convert_holes, TVariable<BoolT>* var_holes_converted) {
DCHECK_NE(first, nullptr);
DCHECK_NE(count, nullptr);
DCHECK_NE(capacity, nullptr);
@ -4097,7 +4137,8 @@ TNode<FixedArray> CodeStubAssembler::ExtractToFixedArray(
AllocationFlag::kNone, var_target_map.value());
var_result.Bind(to_elements);
CopyFixedArrayElements(from_kind, source, to_kind, to_elements, first,
count, capacity, SKIP_WRITE_BARRIER, parameter_mode);
count, capacity, SKIP_WRITE_BARRIER, parameter_mode,
convert_holes, var_holes_converted);
Goto(&done);
if (handle_old_space) {
@ -4111,7 +4152,8 @@ TNode<FixedArray> CodeStubAssembler::ExtractToFixedArray(
var_result.Bind(to_elements);
CopyFixedArrayElements(from_kind, source, to_kind, to_elements, first,
count, capacity, UPDATE_WRITE_BARRIER,
parameter_mode);
parameter_mode, convert_holes,
var_holes_converted);
Goto(&done);
}
}
@ -4121,11 +4163,105 @@ TNode<FixedArray> CodeStubAssembler::ExtractToFixedArray(
return UncheckedCast<FixedArray>(var_result.value());
}
TNode<FixedArrayBase> CodeStubAssembler::ExtractFixedDoubleArrayFillingHoles(
Node* from_array, Node* first, Node* count, Node* capacity,
Node* fixed_array_map, TVariable<BoolT>* var_holes_converted,
AllocationFlags allocation_flags, ExtractFixedArrayFlags extract_flags,
ParameterMode mode) {
DCHECK_NE(first, nullptr);
DCHECK_NE(count, nullptr);
DCHECK_NE(capacity, nullptr);
DCHECK_NE(var_holes_converted, nullptr);
CSA_ASSERT(this, IsFixedDoubleArrayMap(fixed_array_map));
VARIABLE(var_result, MachineRepresentation::kTagged);
ElementsKind kind = PACKED_DOUBLE_ELEMENTS;
Node* to_elements = AllocateFixedArray(kind, capacity, mode, allocation_flags,
fixed_array_map);
var_result.Bind(to_elements);
// We first try to copy the FixedDoubleArray to a new FixedDoubleArray.
// |var_holes_converted| is set to False preliminarily.
*var_holes_converted = Int32FalseConstant();
// The construction of the loop and the offsets for double elements is
// extracted from CopyFixedArrayElements.
CSA_SLOW_ASSERT(this, MatchesParameterMode(count, mode));
CSA_SLOW_ASSERT(this, MatchesParameterMode(capacity, mode));
CSA_SLOW_ASSERT(this, IsFixedArrayWithKindOrEmpty(from_array, kind));
STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize);
Comment("[ ExtractFixedDoubleArrayFillingHoles");
// This copy can trigger GC, so we pre-initialize the array with holes.
FillFixedArrayWithValue(kind, to_elements, IntPtrOrSmiConstant(0, mode),
capacity, RootIndex::kTheHoleValue, mode);
const int first_element_offset = FixedArray::kHeaderSize - kHeapObjectTag;
Node* first_from_element_offset =
ElementOffsetFromIndex(first, kind, mode, 0);
Node* limit_offset = IntPtrAdd(first_from_element_offset,
IntPtrConstant(first_element_offset));
VARIABLE(var_from_offset, MachineType::PointerRepresentation(),
ElementOffsetFromIndex(IntPtrOrSmiAdd(first, count, mode), kind,
mode, first_element_offset));
Label decrement(this, {&var_from_offset}), done(this);
Node* to_array_adjusted =
IntPtrSub(BitcastTaggedToWord(to_elements), first_from_element_offset);
Branch(WordEqual(var_from_offset.value(), limit_offset), &done, &decrement);
BIND(&decrement);
{
Node* from_offset =
IntPtrSub(var_from_offset.value(), IntPtrConstant(kDoubleSize));
var_from_offset.Bind(from_offset);
Node* to_offset = from_offset;
Label if_hole(this);
Node* value = LoadElementAndPrepareForStore(
from_array, var_from_offset.value(), kind, kind, &if_hole);
StoreNoWriteBarrier(MachineRepresentation::kFloat64, to_array_adjusted,
to_offset, value);
Node* compare = WordNotEqual(from_offset, limit_offset);
Branch(compare, &decrement, &done);
BIND(&if_hole);
// We are unlucky: there are holes! We need to restart the copy, this time
// we will copy the FixedDoubleArray to a new FixedArray with undefined
// replacing holes. We signal this to the caller through
// |var_holes_converted|.
*var_holes_converted = Int32TrueConstant();
to_elements =
ExtractToFixedArray(from_array, first, count, capacity, fixed_array_map,
kind, allocation_flags, extract_flags, mode,
HoleConversionMode::kConvertToUndefined);
var_result.Bind(to_elements);
Goto(&done);
}
BIND(&done);
Comment("] ExtractFixedDoubleArrayFillingHoles");
return UncheckedCast<FixedArrayBase>(var_result.value());
}
TNode<FixedArrayBase> CodeStubAssembler::ExtractFixedArray(
Node* source, Node* first, Node* count, Node* capacity,
ExtractFixedArrayFlags extract_flags, ParameterMode parameter_mode) {
ExtractFixedArrayFlags extract_flags, ParameterMode parameter_mode,
TVariable<BoolT>* var_holes_converted) {
DCHECK(extract_flags & ExtractFixedArrayFlag::kFixedArrays ||
extract_flags & ExtractFixedArrayFlag::kFixedDoubleArrays);
// If we want to replace holes, ExtractFixedArrayFlag::kDontCopyCOW should not
// be used, because that disables the iteration which detects holes.
DCHECK_IMPLIES(var_holes_converted != nullptr,
!(extract_flags & ExtractFixedArrayFlag::kDontCopyCOW));
HoleConversionMode convert_holes =
var_holes_converted != nullptr ? HoleConversionMode::kConvertToUndefined
: HoleConversionMode::kDontConvert;
VARIABLE(var_result, MachineRepresentation::kTagged);
const AllocationFlags allocation_flags =
(extract_flags & ExtractFixedArrayFlag::kNewSpaceAllocationOnly)
@ -4164,27 +4300,36 @@ TNode<FixedArrayBase> CodeStubAssembler::ExtractFixedArray(
}
if (extract_flags & ExtractFixedArrayFlag::kFixedArrays) {
// Here we can only get source as FixedArray, never FixedDoubleArray.
// Here we can only get |source| as FixedArray, never FixedDoubleArray.
// PACKED_ELEMENTS is used to signify that the source is a FixedArray.
Node* to_elements = ExtractToFixedArray(
source, first, count, capacity, source_map, PACKED_ELEMENTS,
allocation_flags, extract_flags, parameter_mode);
Node* to_elements =
ExtractToFixedArray(source, first, count, capacity, source_map,
PACKED_ELEMENTS, allocation_flags, extract_flags,
parameter_mode, convert_holes, var_holes_converted);
var_result.Bind(to_elements);
Goto(&done);
}
if (extract_flags & ExtractFixedArrayFlag::kFixedDoubleArrays) {
BIND(&if_fixed_double_array);
Comment("Copy FixedDoubleArray");
// We use PACKED_DOUBLE_ELEMENTS to signify that the source is
// FixedDoubleArray. That it is PACKED or HOLEY does not matter.
ElementsKind kind = PACKED_DOUBLE_ELEMENTS;
Node* to_elements = AllocateFixedArray(kind, capacity, parameter_mode,
allocation_flags, source_map);
var_result.Bind(to_elements);
CopyFixedArrayElements(kind, source, kind, to_elements, first, count,
capacity, SKIP_WRITE_BARRIER, parameter_mode);
if (convert_holes == HoleConversionMode::kConvertToUndefined) {
Node* to_elements = ExtractFixedDoubleArrayFillingHoles(
source, first, count, capacity, source_map, var_holes_converted,
allocation_flags, extract_flags, parameter_mode);
var_result.Bind(to_elements);
} else {
// We use PACKED_DOUBLE_ELEMENTS to signify that both the source and
// the target are FixedDoubleArray. That it is PACKED or HOLEY does not
// matter.
ElementsKind kind = PACKED_DOUBLE_ELEMENTS;
Node* to_elements = AllocateFixedArray(kind, capacity, parameter_mode,
allocation_flags, source_map);
var_result.Bind(to_elements);
CopyFixedArrayElements(kind, source, kind, to_elements, first, count,
capacity, SKIP_WRITE_BARRIER, parameter_mode);
}
Goto(&done);
}
@ -4354,7 +4499,10 @@ void CodeStubAssembler::FillFixedDoubleArrayWithZero(
void CodeStubAssembler::CopyFixedArrayElements(
ElementsKind from_kind, Node* from_array, ElementsKind to_kind,
Node* to_array, Node* first_element, Node* element_count, Node* capacity,
WriteBarrierMode barrier_mode, ParameterMode mode) {
WriteBarrierMode barrier_mode, ParameterMode mode,
HoleConversionMode convert_holes, TVariable<BoolT>* var_holes_converted) {
DCHECK_IMPLIES(var_holes_converted != nullptr,
convert_holes == HoleConversionMode::kConvertToUndefined);
CSA_SLOW_ASSERT(this, MatchesParameterMode(element_count, mode));
CSA_SLOW_ASSERT(this, MatchesParameterMode(capacity, mode));
CSA_SLOW_ASSERT(this, IsFixedArrayWithKindOrEmpty(from_array, from_kind));
@ -4382,10 +4530,20 @@ void CodeStubAssembler::CopyFixedArrayElements(
Is64() ? ReinterpretCast<UintPtrT>(Int64Constant(kHoleNanInt64))
: ReinterpretCast<UintPtrT>(Int32Constant(kHoleNanLower32));
if (doubles_to_objects_conversion) {
// If the copy might trigger a GC, make sure that the FixedArray is
// pre-initialized with holes to make sure that it's always in a
// consistent state.
// If copying might trigger a GC, we pre-initialize the FixedArray such that
// it's always in a consistent state.
if (convert_holes == HoleConversionMode::kConvertToUndefined) {
DCHECK(IsObjectElementsKind(to_kind));
// Use undefined for the part that we copy and holes for the rest.
// Later if we run into a hole in the source we can just skip the writing
// to the target and are still guaranteed that we get an undefined.
FillFixedArrayWithValue(to_kind, to_array, IntPtrOrSmiConstant(0, mode),
element_count, RootIndex::kUndefinedValue, mode);
FillFixedArrayWithValue(to_kind, to_array, element_count, capacity,
RootIndex::kTheHoleValue, mode);
} else if (doubles_to_objects_conversion) {
// Pre-initialized the target with holes so later if we run into a hole in
// the source we can just skip the writing to the target.
FillFixedArrayWithValue(to_kind, to_array, IntPtrOrSmiConstant(0, mode),
capacity, RootIndex::kTheHoleValue, mode);
} else if (element_count != capacity) {
@ -4411,8 +4569,10 @@ void CodeStubAssembler::CopyFixedArrayElements(
first_element_offset));
}
Variable* vars[] = {&var_from_offset, &var_to_offset};
Label decrement(this, 2, vars);
Variable* vars[] = {&var_from_offset, &var_to_offset, var_holes_converted};
int num_vars =
var_holes_converted != nullptr ? arraysize(vars) : arraysize(vars) - 1;
Label decrement(this, num_vars, vars);
Node* to_array_adjusted =
element_offset_matches
@ -4438,9 +4598,13 @@ void CodeStubAssembler::CopyFixedArrayElements(
var_to_offset.Bind(to_offset);
}
Label next_iter(this), store_double_hole(this);
Label next_iter(this), store_double_hole(this), signal_hole(this);
Label* if_hole;
if (doubles_to_objects_conversion) {
if (convert_holes == HoleConversionMode::kConvertToUndefined) {
// The target elements array is already preinitialized with undefined
// so we only need to signal that a hole was found and continue the loop.
if_hole = &signal_hole;
} else if (doubles_to_objects_conversion) {
// The target elements array is already preinitialized with holes, so we
// can just proceed with the next iteration.
if_hole = &next_iter;
@ -4487,6 +4651,13 @@ void CodeStubAssembler::CopyFixedArrayElements(
double_hole);
}
Goto(&next_iter);
} else if (if_hole == &signal_hole) {
// This case happens only when IsObjectElementsKind(to_kind).
BIND(&signal_hole);
if (var_holes_converted != nullptr) {
*var_holes_converted = Int32TrueConstant();
}
Goto(&next_iter);
}
BIND(&next_iter);

View File

@ -1411,9 +1411,21 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
INTPTR_PARAMETERS);
}
Node* CloneFastJSArray(Node* context, Node* array,
ParameterMode mode = INTPTR_PARAMETERS,
Node* allocation_site = nullptr);
enum class HoleConversionMode { kDontConvert, kConvertToUndefined };
// Clone a fast JSArray |array| into a new fast JSArray.
// |convert_holes| tells the function to convert holes into undefined or not.
// If |convert_holes| is set to kConvertToUndefined, but the function did not
// find any hole in |array|, the resulting array will have the same elements
// kind as |array|. If the function did find a hole, it will convert holes in
// |array| to undefined in the resulting array, who will now have
// PACKED_ELEMENTS kind.
// If |convert_holes| is set kDontConvert, holes are also copied to the
// resulting array, who will have the same elements kind as |array|. The
// function generates significantly less code in this case.
Node* CloneFastJSArray(
Node* context, Node* array, ParameterMode mode = INTPTR_PARAMETERS,
Node* allocation_site = nullptr,
HoleConversionMode convert_holes = HoleConversionMode::kDontConvert);
Node* ExtractFastJSArray(Node* context, Node* array, Node* begin, Node* count,
ParameterMode mode = INTPTR_PARAMETERS,
@ -1511,11 +1523,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Copies |element_count| elements from |from_array| starting from element
// |first_element| to |to_array| of |capacity| size respecting both array's
// elements kinds.
// |convert_holes| tells the function whether to convert holes to undefined.
// |var_holes_converted| can be used to signify that the conversion happened
// (i.e. that there were holes). If |convert_holes_to_undefined| is
// HoleConversionMode::kConvertToUndefined, then it must not be the case that
// IsDoubleElementsKind(to_kind).
void CopyFixedArrayElements(
ElementsKind from_kind, Node* from_array, ElementsKind to_kind,
Node* to_array, Node* first_element, Node* element_count, Node* capacity,
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
ParameterMode mode = INTPTR_PARAMETERS);
ParameterMode mode = INTPTR_PARAMETERS,
HoleConversionMode convert_holes = HoleConversionMode::kDontConvert,
TVariable<BoolT>* var_holes_converted = nullptr);
void CopyFixedArrayElements(
ElementsKind from_kind, TNode<FixedArrayBase> from_array,
@ -1575,12 +1594,16 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// passed as the |source| parameter.
// * |parameter_mode| determines the parameter mode of |first|, |count| and
// |capacity|.
// * If |var_holes_converted| is given, any holes will be converted to
// undefined and the variable will be set according to whether or not there
// were any hole.
TNode<FixedArrayBase> ExtractFixedArray(
Node* source, Node* first, Node* count = nullptr,
Node* capacity = nullptr,
ExtractFixedArrayFlags extract_flags =
ExtractFixedArrayFlag::kAllFixedArrays,
ParameterMode parameter_mode = INTPTR_PARAMETERS);
ParameterMode parameter_mode = INTPTR_PARAMETERS,
TVariable<BoolT>* var_holes_converted = nullptr);
TNode<FixedArrayBase> ExtractFixedArray(
TNode<FixedArrayBase> source, TNode<Smi> first, TNode<Smi> count,
@ -1606,14 +1629,49 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// non-double, so as to distinguish FixedDoubleArray vs. FixedArray. It does
// not care about holeyness. For example, when |source| is a FixedArray,
// PACKED/HOLEY_ELEMENTS can be used, but not PACKED_DOUBLE_ELEMENTS.
// * The function uses |allocation_flags| and |extract_flags| to decide how to
// allocate the result FixedArray.
// * |allocation_flags| and |extract_flags| influence how the target
// FixedArray is allocated.
// * |parameter_mode| determines the parameter mode of |first|, |count| and
// |capacity|.
// * |convert_holes| is used to signify that the target array should use
// undefined in places of holes.
// * If |convert_holes| is true and |var_holes_converted| not nullptr, then
// |var_holes_converted| is used to signal whether any holes were found and
// converted. The caller should use this information to decide which map is
// compatible with the result array. For example, if the input was of
// HOLEY_SMI_ELEMENTS kind, and a conversion took place, the result will be
// compatible only with HOLEY_ELEMENTS and PACKED_ELEMENTS.
TNode<FixedArray> ExtractToFixedArray(
Node* source, Node* first, Node* count, Node* capacity, Node* source_map,
ElementsKind from_kind = PACKED_ELEMENTS,
AllocationFlags allocation_flags = AllocationFlag::kNone,
ExtractFixedArrayFlags extract_flags =
ExtractFixedArrayFlag::kAllFixedArrays,
ParameterMode parameter_mode = INTPTR_PARAMETERS,
HoleConversionMode convert_holes = HoleConversionMode::kDontConvert,
TVariable<BoolT>* var_holes_converted = nullptr);
// Attempt to copy a FixedDoubleArray to another FixedDoubleArray. In the case
// where the source array has a hole, produce a FixedArray instead where holes
// are replaced with undefined.
// * |source| is a FixedDoubleArray from which to copy elements.
// * |first| is the starting element index to copy from.
// * |count| is the number of elements to copy out of the source array
// starting from and including the element indexed by |start|.
// * |capacity| determines the size of the allocated result array, with
// |capacity| >= |count|.
// * |source_map| is the map of |source|. It will be used as the map of the
// target array if the target can stay a FixedDoubleArray. Otherwise if the
// target array needs to be a FixedArray, the FixedArrayMap will be used.
// * |var_holes_converted| is used to signal whether a FixedAray
// is produced or not.
// * |allocation_flags| and |extract_flags| influence how the target array is
// allocated.
// * |parameter_mode| determines the parameter mode of |first|, |count| and
// |capacity|.
TNode<FixedArrayBase> ExtractFixedDoubleArrayFillingHoles(
Node* source, Node* first, Node* count, Node* capacity, Node* source_map,
TVariable<BoolT>* var_holes_converted, AllocationFlags allocation_flags,
ExtractFixedArrayFlags extract_flags =
ExtractFixedArrayFlag::kAllFixedArrays,
ParameterMode parameter_mode = INTPTR_PARAMETERS);