Commit 7c35c03b authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Fast path TA.p.subarray creating TypedArray when default constructor is used

Previously, the following call sequence was always made when creating resulting
subsetted TypedArray:
1) TFJ TypedArrayPrototypeSubArray
2) TFS TypedArrayConstructor
3) TFS CreateTypedArray

This CL, skips #2 and goes straight to #3 when the default constructor (builtin) is
safe to use (IsPrototypeTypedArrayPrototype and
!IsTypedArraySpeciesProtectorCellInvalid).

Local TypedArrays/SubarrayNoSpecies microbenchmark shows ~35-40% improvement...

BEFORE
TypedArrays-SubarrayNoSpecies(Score): 1033530
TypedArrays-SubarrayNoSpecies(Score): 1018490
TypedArrays-SubarrayNoSpecies(Score): 1037030

AFTER
TypedArrays-SubarrayNoSpecies(Score): 1439030
TypedArrays-SubarrayNoSpecies(Score): 1417540
TypedArrays-SubarrayNoSpecies(Score): 1405980

Bug: v8:7161
Change-Id: I356dace36570aa161ffe208a57a80e46714121a2
Reviewed-on: https://chromium-review.googlesource.com/c/1331154
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57458}
parent a377c9ad
......@@ -208,7 +208,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
const char* method_name = "%TypedArray%.prototype.map";
TypedArrayBuiltinsAssembler typedarray_asm(state());
TNode<JSTypedArray> a = typedarray_asm.SpeciesCreateByLength(
TNode<JSTypedArray> a = typedarray_asm.TypedArraySpeciesCreateByLength(
context(), original_array, length, method_name);
// In the Spec and our current implementation, the length check is already
// performed in TypedArraySpeciesCreate.
......
......@@ -885,59 +885,77 @@ TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
LoadContextElement(LoadNativeContext(context), context_slot.value()));
}
TNode<JSReceiver> TypedArrayBuiltinsAssembler::TypedArraySpeciesConstructor(
TNode<Context> context, TNode<JSTypedArray> exemplar) {
TVARIABLE(JSReceiver, var_constructor);
Label slow(this), done(this);
template <class... TArgs>
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArraySpeciesCreate(
const char* method_name, TNode<Context> context,
TNode<JSTypedArray> exemplar, TArgs... args) {
TVARIABLE(JSTypedArray, var_new_typed_array);
Label slow(this, Label::kDeferred), done(this);
// Let defaultConstructor be the intrinsic object listed in column one of
// Table 52 for exemplar.[[TypedArrayName]].
TNode<JSFunction> default_constructor =
GetDefaultConstructor(context, exemplar);
var_constructor = default_constructor;
Node* map = LoadMap(exemplar);
TNode<Map> map = LoadMap(exemplar);
GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow);
Branch(IsTypedArraySpeciesProtectorCellInvalid(), &slow, &done);
BIND(&slow);
var_constructor = SpeciesConstructor(context, exemplar, default_constructor);
GotoIf(IsTypedArraySpeciesProtectorCellInvalid(), &slow);
{
const size_t argc = sizeof...(args);
static_assert(argc >= 1 && argc <= 3,
"TypedArraySpeciesCreate called with unexpected arguments");
TNode<Object> arg_list[argc] = {args...};
TNode<Object> arg0 = argc < 1 ? UndefinedConstant() : arg_list[0];
TNode<Object> arg1 = argc < 2 ? UndefinedConstant() : arg_list[1];
TNode<Object> arg2 = argc < 3 ? UndefinedConstant() : arg_list[2];
var_new_typed_array = UncheckedCast<JSTypedArray>(
CallBuiltin(Builtins::kCreateTypedArray, context, default_constructor,
default_constructor, arg0, arg1, arg2));
#ifdef DEBUG
// It is assumed that the CreateTypedArray builtin does not produce a
// typed array that fails ValidateTypedArray.
TNode<JSArrayBuffer> buffer =
LoadJSArrayBufferViewBuffer(var_new_typed_array.value());
CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(buffer)));
#endif // DEBUG
Goto(&done);
BIND(&done);
return var_constructor.value();
}
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::SpeciesCreateByArrayBuffer(
TNode<Context> context, TNode<JSTypedArray> exemplar,
TNode<JSArrayBuffer> buffer, TNode<Number> byte_offset, TNode<Smi> len,
const char* method_name) {
}
BIND(&slow);
{
// Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
TNode<JSReceiver> constructor =
TypedArraySpeciesConstructor(context, exemplar);
SpeciesConstructor(context, exemplar, default_constructor);
// Let newTypedArray be ? Construct(constructor, argumentList).
TNode<JSReceiver> new_object =
Construct(context, constructor, buffer, byte_offset, len);
TNode<JSReceiver> new_object = Construct(context, constructor, args...);
// Perform ? ValidateTypedArray(newTypedArray).
return ValidateTypedArray(context, new_object, method_name);
var_new_typed_array = ValidateTypedArray(context, new_object, method_name);
Goto(&done);
}
BIND(&done);
return var_new_typed_array.value();
}
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::SpeciesCreateByLength(
TNode<JSTypedArray>
TypedArrayBuiltinsAssembler::TypedArraySpeciesCreateByLength(
TNode<Context> context, TNode<JSTypedArray> exemplar, TNode<Smi> len,
const char* method_name) {
CSA_ASSERT(this, TaggedIsPositiveSmi(len));
// Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
TNode<JSReceiver> constructor =
TypedArraySpeciesConstructor(context, exemplar);
return CreateByLength(context, constructor, len, method_name);
TNode<JSTypedArray> new_typed_array =
TypedArraySpeciesCreate(method_name, context, exemplar, len);
ThrowIfLengthLessThan(context, new_typed_array, len);
return new_typed_array;
}
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::CreateByLength(
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArrayCreateByLength(
TNode<Context> context, TNode<Object> constructor, TNode<Smi> len,
const char* method_name) {
CSA_ASSERT(this, TaggedIsPositiveSmi(len));
// Let newTypedArray be ? Construct(constructor, argumentList).
TNode<Object> new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()),
context, constructor, len));
......@@ -946,15 +964,20 @@ TNode<JSTypedArray> TypedArrayBuiltinsAssembler::CreateByLength(
TNode<JSTypedArray> new_typed_array =
ValidateTypedArray(context, new_object, method_name);
// If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError
// exception.
ThrowIfLengthLessThan(context, new_typed_array, len);
return new_typed_array;
}
void TypedArrayBuiltinsAssembler::ThrowIfLengthLessThan(
TNode<Context> context, TNode<JSTypedArray> typed_array,
TNode<Smi> min_length) {
// If typed_array.[[ArrayLength]] < min_length, throw a TypeError exception.
Label if_length_is_not_short(this);
TNode<Smi> new_length = LoadJSTypedArrayLength(new_typed_array);
GotoIfNot(SmiLessThan(new_length, len), &if_length_is_not_short);
TNode<Smi> new_length = LoadJSTypedArrayLength(typed_array);
GotoIfNot(SmiLessThan(new_length, min_length), &if_length_is_not_short);
ThrowTypeError(context, MessageTemplate::kTypedArrayTooShort);
BIND(&if_length_is_not_short);
return new_typed_array;
}
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
......@@ -1313,7 +1336,7 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
// Create a result array by invoking TypedArraySpeciesCreate.
TNode<Smi> count = SmiMax(SmiSub(end_index, start_index), SmiConstant(0));
TNode<JSTypedArray> result_array =
SpeciesCreateByLength(context, source, count, method_name);
TypedArraySpeciesCreateByLength(context, source, count, method_name);
// If count is zero, return early.
GotoIf(SmiGreaterThan(count, SmiConstant(0)), &if_count_is_not_zero);
......@@ -1456,8 +1479,8 @@ TF_BUILTIN(TypedArrayPrototypeSubArray, TypedArrayBuiltinsAssembler) {
// 16. Let argumentsList be « buffer, beginByteOffset, newLength ».
// 17. Return ? TypedArraySpeciesCreate(O, argumentsList).
args.PopAndReturn(SpeciesCreateByArrayBuffer(
context, source, buffer, begin_byte_offset, new_length, method_name));
args.PopAndReturn(TypedArraySpeciesCreate(
method_name, context, source, buffer, begin_byte_offset, new_length));
}
// ES #sec-get-%typedarray%.prototype-@@tostringtag
......@@ -1567,8 +1590,8 @@ TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
// 5. Let newObj be ? TypedArrayCreate(C, len).
TNode<JSTypedArray> new_typed_array =
CreateByLength(context, receiver, SmiTag(length), "%TypedArray%.of");
TNode<JSTypedArray> new_typed_array = TypedArrayCreateByLength(
context, receiver, SmiTag(length), "%TypedArray%.of");
TNode<Word32T> elements_kind = LoadElementsKind(new_typed_array);
......@@ -1780,8 +1803,8 @@ TF_BUILTIN(TypedArrayFrom, TypedArrayBuiltinsAssembler) {
BIND(&create_typed_array);
{
// 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»).
target_obj = CreateByLength(context, receiver, final_length.value(),
"%TypedArray%.from");
target_obj = TypedArrayCreateByLength(
context, receiver, final_length.value(), "%TypedArray%.from");
Branch(mapping.value(), &slow_path, &fast_path);
}
......@@ -1940,7 +1963,7 @@ TF_BUILTIN(TypedArrayPrototypeFilter, TypedArrayBuiltinsAssembler) {
// 10. Let A be ? TypedArraySpeciesCreate(O, captured).
TNode<JSTypedArray> result_array =
SpeciesCreateByLength(context, source, captured, method_name);
TypedArraySpeciesCreateByLength(context, source, captured, method_name);
// 11. Let n be 0.
// 12. For each element e of kept, do
......
......@@ -15,9 +15,14 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
TNode<JSTypedArray> SpeciesCreateByLength(TNode<Context> context,
template <class... TArgs>
TNode<JSTypedArray> TypedArraySpeciesCreate(const char* method_name,
TNode<Context> context,
TNode<JSTypedArray> exemplar,
TNode<Smi> len,
TArgs... args);
TNode<JSTypedArray> TypedArraySpeciesCreateByLength(
TNode<Context> context, TNode<JSTypedArray> exemplar, TNode<Smi> len,
const char* method_name);
void GenerateTypedArrayPrototypeIterationMethod(TNode<Context> context,
......@@ -75,19 +80,14 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<JSFunction> GetDefaultConstructor(TNode<Context> context,
TNode<JSTypedArray> exemplar);
TNode<JSReceiver> TypedArraySpeciesConstructor(TNode<Context> context,
TNode<JSTypedArray> exemplar);
TNode<JSTypedArray> SpeciesCreateByArrayBuffer(TNode<Context> context,
TNode<JSTypedArray> exemplar,
TNode<JSArrayBuffer> buffer,
TNode<Number> byte_offset,
TNode<JSTypedArray> TypedArrayCreateByLength(TNode<Context> context,
TNode<Object> constructor,
TNode<Smi> len,
const char* method_name);
TNode<JSTypedArray> CreateByLength(TNode<Context> context,
TNode<Object> constructor, TNode<Smi> len,
const char* method_name);
void ThrowIfLengthLessThan(TNode<Context> context,
TNode<JSTypedArray> typed_array,
TNode<Smi> min_length);
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
TNode<JSTypedArray> array);
......
......@@ -1022,3 +1022,29 @@ assertThrows(function LargeSourceArray() {
a.set(v0);
});
function TestMapCustomSpeciesConstructor(constructor) {
const sample = new constructor([40, 42, 42]);
let result, ctorThis;
sample.constructor = {};
sample.constructor[Symbol.species] = function(count) {
result = arguments;
ctorThis = this;
return new constructor(count);
};
sample.map(function(v) { return v; });
assertSame(result.length, 1, "called with 1 argument");
assertSame(result[0], 3, "[0] is the new captured length");
assertTrue(
ctorThis instanceof sample.constructor[Symbol.species],
"`this` value in the @@species fn is an instance of the function itself"
);
};
for(i = 0; i < typedArrayConstructors.length; i++) {
TestPropertyTypeChecks(typedArrayConstructors[i]);
}
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