Commit 6e857421 authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

Optimize array clone for sealed, frozen objects

Improve micro-benchmark by ~5x
Before:
ApplySpreadLiteral
ApplySpreadLiteral-Numbers(Score): 279
SpreadCallSpreadLiteral
SpreadCallSpreadLiteral-Numbers(Score): 285

After:
ApplySpreadLiteral
ApplySpreadLiteral-Numbers(Score): 1074
SpreadCallSpreadLiteral
SpreadCallSpreadLiteral-Numbers(Score): 1009


Bug: v8:6831
Change-Id: Ifd676ca13d5b7e86afc1578636fdd4dc2733c474
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1628244
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61862}
parent d3e96993
......@@ -434,6 +434,11 @@ transient type FastJSArrayForCopy extends FastJSArray;
// A FastJSArray when the global ArrayIteratorProtector is not invalidated.
transient type FastJSArrayWithNoCustomIteration extends FastJSArray;
// A FastJSArrayForRead when the global ArrayIteratorProtector is not
// invalidated.
transient type FastJSArrayForReadWithNoCustomIteration extends
FastJSArrayForRead;
type NoSharedNameSentinel extends Smi;
type JSModuleNamespace extends JSObject;
......@@ -1950,6 +1955,14 @@ Cast<FastJSArrayWithNoCustomIteration>(implicit context: Context)(
return %RawDownCast<FastJSArrayWithNoCustomIteration>(o);
}
Cast<FastJSArrayForReadWithNoCustomIteration>(implicit context: Context)(
o: HeapObject): FastJSArrayForReadWithNoCustomIteration
labels CastError {
if (IsArrayIteratorProtectorCellInvalid()) goto CastError;
const a: FastJSArrayForRead = Cast<FastJSArrayForRead>(o) otherwise CastError;
return %RawDownCast<FastJSArrayForReadWithNoCustomIteration>(o);
}
Cast<JSReceiver>(implicit context: Context)(o: HeapObject): JSReceiver
labels CastError {
if (IsJSReceiver(o)) return %RawDownCast<JSReceiver>(o);
......@@ -2856,6 +2869,12 @@ macro IsFastJSArrayWithNoCustomIteration(context: Context, o: Object): bool {
return Is<FastJSArrayWithNoCustomIteration>(o);
}
@export
macro IsFastJSArrayForReadWithNoCustomIteration(context: Context, o: Object):
bool {
return Is<FastJSArrayForReadWithNoCustomIteration>(o);
}
extern transitioning runtime
CreateDataProperty(implicit context: Context)(JSReceiver, Object, Object);
......
......@@ -553,8 +553,8 @@ TF_BUILTIN(CloneFastJSArray, ArrayBuiltinsAssembler) {
TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
CSA_ASSERT(this,
Word32Or(Word32BinaryNot(
IsHoleyFastElementsKind(LoadElementsKind(array))),
Word32Or(Word32BinaryNot(IsHoleyFastElementsKindForRead(
LoadElementsKind(array))),
Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
ParameterMode mode = OptimalParameterMode();
......@@ -573,8 +573,8 @@ TF_BUILTIN(CloneFastJSArrayFillingHoles, ArrayBuiltinsAssembler) {
TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
CSA_ASSERT(this,
Word32Or(Word32BinaryNot(
IsHoleyFastElementsKind(LoadElementsKind(array))),
Word32Or(Word32BinaryNot(IsHoleyFastElementsKindForRead(
LoadElementsKind(array))),
Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
ParameterMode mode = OptimalParameterMode();
......
......@@ -270,8 +270,10 @@ void IteratorBuiltinsAssembler::FastIterableToList(
TVariable<Object>* var_result, Label* slow) {
Label done(this), check_string(this), check_map(this), check_set(this);
GotoIfNot(IsFastJSArrayWithNoCustomIteration(context, iterable),
&check_string);
GotoIfNot(
Word32Or(IsFastJSArrayWithNoCustomIteration(context, iterable),
IsFastJSArrayForReadWithNoCustomIteration(context, iterable)),
&check_string);
// Fast path for fast JSArray.
*var_result =
......
......@@ -4076,13 +4076,15 @@ Node* CodeStubAssembler::CloneFastJSArray(Node* context, Node* array,
VARIABLE(var_new_elements, MachineRepresentation::kTagged);
TVARIABLE(Int32T, var_elements_kind, LoadMapElementsKind(LoadMap(array)));
Label allocate_jsarray(this), holey_extract(this);
Label allocate_jsarray(this), holey_extract(this),
allocate_jsarray_main(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);
GotoIf(IsHoleyFastElementsKindForRead(var_elements_kind.value()),
&holey_extract);
}
// Simple extraction that preserves holes.
......@@ -4117,6 +4119,17 @@ Node* CodeStubAssembler::CloneFastJSArray(Node* context, Node* array,
}
BIND(&allocate_jsarray);
// Handle sealed, frozen elements kinds
CSA_ASSERT(this, IsElementsKindLessThanOrEqual(var_elements_kind.value(),
LAST_FROZEN_ELEMENTS_KIND));
GotoIf(IsElementsKindLessThanOrEqual(var_elements_kind.value(),
LAST_FAST_ELEMENTS_KIND),
&allocate_jsarray_main);
var_elements_kind = Int32Constant(PACKED_ELEMENTS);
Goto(&allocate_jsarray_main);
BIND(&allocate_jsarray_main);
// Use the cannonical map for the chosen elements kind.
Node* native_context = LoadNativeContext(context);
TNode<Map> array_map =
......@@ -13396,6 +13409,19 @@ Node* CodeStubAssembler::IsHoleyFastElementsKind(Node* elements_kind) {
return IsSetWord32(elements_kind, 1);
}
Node* CodeStubAssembler::IsHoleyFastElementsKindForRead(Node* elements_kind) {
CSA_ASSERT(this,
Uint32LessThanOrEqual(elements_kind,
Int32Constant(LAST_FROZEN_ELEMENTS_KIND)));
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == (PACKED_SMI_ELEMENTS | 1));
STATIC_ASSERT(HOLEY_ELEMENTS == (PACKED_ELEMENTS | 1));
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == (PACKED_DOUBLE_ELEMENTS | 1));
STATIC_ASSERT(HOLEY_SEALED_ELEMENTS == (PACKED_SEALED_ELEMENTS | 1));
STATIC_ASSERT(HOLEY_FROZEN_ELEMENTS == (PACKED_FROZEN_ELEMENTS | 1));
return IsSetWord32(elements_kind, 1);
}
Node* CodeStubAssembler::IsElementsKindGreaterThan(
Node* target_kind, ElementsKind reference_kind) {
return Int32GreaterThan(target_kind, Int32Constant(reference_kind));
......
......@@ -2365,6 +2365,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
Node* IsFastSmiOrTaggedElementsKind(Node* elements_kind);
Node* IsFastSmiElementsKind(Node* elements_kind);
Node* IsHoleyFastElementsKind(Node* elements_kind);
Node* IsHoleyFastElementsKindForRead(Node* elements_kind);
Node* IsElementsKindGreaterThan(Node* target_kind,
ElementsKind reference_kind);
TNode<BoolT> IsElementsKindLessThanOrEqual(TNode<Int32T> target_kind,
......
......@@ -777,3 +777,19 @@ assertEquals(arrSpread.length, arrLike.length);
assertEquals(arrSpread[0], 'a');
assertEquals(arrSpread[1], 'b');
assertEquals(arrSpread[2], 'c');
// Spread with holey
function countArgs() {
return arguments.length;
}
var arr = [, 'b','c'];
Object.freeze(arr);
assertEquals(countArgs(...arr), 3);
assertEquals(countArgs(...[...arr]), 3);
assertEquals(countArgs.apply(this, [...arr]), 3);
function checkUndefined() {
return arguments[0] === undefined;
}
assertTrue(checkUndefined(...arr));
assertTrue(checkUndefined(...[...arr]));
assertTrue(checkUndefined.apply(this, [...arr]));
......@@ -441,3 +441,19 @@ assertEquals(arrSpread.length, arrLike.length);
assertEquals(arrSpread[0], 'a');
assertEquals(arrSpread[1], 'b');
assertEquals(arrSpread[2], 'c');
// Spread with holey
function countArgs() {
return arguments.length;
}
var arr = [, 'b','c'];
Object.preventExtensions(arr);
assertEquals(countArgs(...arr), 3);
assertEquals(countArgs(...[...arr]), 3);
assertEquals(countArgs.apply(this, [...arr]), 3);
function checkUndefined() {
return arguments[0] === undefined;
}
assertTrue(checkUndefined(...arr));
assertTrue(checkUndefined(...[...arr]));
assertTrue(checkUndefined.apply(this, [...arr]));
......@@ -749,3 +749,19 @@ assertEquals(arrSpread.length, arrLike.length);
assertEquals(arrSpread[0], 'a');
assertEquals(arrSpread[1], 'b');
assertEquals(arrSpread[2], 'c');
// Spread with holey
function countArgs() {
return arguments.length;
}
var arr = [, 'b','c'];
Object.seal(arr);
assertEquals(countArgs(...arr), 3);
assertEquals(countArgs(...[...arr]), 3);
assertEquals(countArgs.apply(this, [...arr]), 3);
function checkUndefined() {
return arguments[0] === undefined;
}
assertTrue(checkUndefined(...arr));
assertTrue(checkUndefined(...[...arr]));
assertTrue(checkUndefined.apply(this, [...arr]));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment