Commit 8fbc6a05 authored by Choongwoo Han's avatar Choongwoo Han Committed by Commit Bot

Optimize TypedArraySpeciesCreate using SpeciesProtector of Array

If there is no constructor or species updates on Array or TypedArrays,
then skip lookups of constructor and species so that we can create a new
typed array quickly. This path makes TA.p.slice() 4x faster in fast
cases.

Bug: v8:7161
Change-Id: Ib8d2a3f6b8b5ed356c5822a814164166d1285f64
Reviewed-on: https://chromium-review.googlesource.com/828343
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50423}
parent 96d869fd
......@@ -237,22 +237,45 @@ void LookupIterator::ReloadPropertyInformation() {
DCHECK(IsFound() || !holder_->HasFastProperties());
}
namespace {
bool IsTypedArrayFunctionInAnyContext(Isolate* isolate, JSReceiver* holder) {
static uint32_t context_slots[] = {
#define TYPED_ARRAY_CONTEXT_SLOTS(Type, type, TYPE, ctype, size) \
Context::TYPE##_ARRAY_FUN_INDEX,
TYPED_ARRAYS(TYPED_ARRAY_CONTEXT_SLOTS)
#undef TYPED_ARRAY_CONTEXT_SLOTS
};
if (!holder->IsJSFunction()) return false;
return std::any_of(
std::begin(context_slots), std::end(context_slots),
[=](uint32_t slot) { return isolate->IsInAnyContext(holder, slot); });
}
} // namespace
void LookupIterator::InternalUpdateProtector() {
if (isolate_->bootstrapper()->IsActive()) return;
if (*name_ == heap()->constructor_string()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
// Setting the constructor property could change an instance's @@species
if (holder_->IsJSArray()) {
if (holder_->IsJSArray() || holder_->IsJSTypedArray()) {
isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
isolate_->InvalidateArraySpeciesProtector();
} else if (holder_->map()->is_prototype_map()) {
DisallowHeapAllocation no_gc;
// Setting the constructor of Array.prototype of any realm also needs
// to invalidate the species protector
// Setting the constructor of Array.prototype or %TypedArray%.prototype of
// any realm also needs to invalidate the species protector.
// For typed arrays, we check a prototype of this holder since TypedArrays
// have different prototypes for each type, and their parent prototype is
// pointing the same TYPED_ARRAY_PROTOTYPE.
if (isolate_->IsInAnyContext(*holder_,
Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
Context::INITIAL_ARRAY_PROTOTYPE_INDEX) ||
isolate_->IsInAnyContext(holder_->map()->prototype(),
Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
isolate_->CountUsage(v8::Isolate::UseCounterFeature::
kArrayPrototypeConstructorModified);
isolate_->InvalidateArraySpeciesProtector();
......@@ -260,9 +283,10 @@ void LookupIterator::InternalUpdateProtector() {
}
} else if (*name_ == heap()->species_symbol()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
// Setting the Symbol.species property of any Array constructor invalidates
// the species protector
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
// Setting the Symbol.species property of any Array or TypedArray
// constructor invalidates the species protector
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX) ||
IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArraySpeciesModified);
isolate_->InvalidateArraySpeciesProtector();
......
......@@ -16674,6 +16674,47 @@ MaybeHandle<JSTypedArray> JSTypedArray::Create(Isolate* isolate,
return new_array;
}
// static
MaybeHandle<JSTypedArray> JSTypedArray::CreateFast(
Isolate* isolate, Handle<JSTypedArray> exemplar, int argc,
Handle<Object>* argv, const char* method_name) {
DCHECK_GT(argc, 0);
DCHECK_IMPLIES(argc == 1, argv[0]->IsNumber());
DCHECK_IMPLIES(argc == 3, argv[0]->IsJSArrayBuffer());
DCHECK_IMPLIES(argc == 3, argv[1]->IsNumber());
DCHECK_IMPLIES(argc == 3, argv[2]->IsNumber());
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
Handle<JSTypedArray> new_array;
if (argc == 1) {
size_t length = NumberToSize(*argv[0]);
new_array = isolate->factory()->NewJSTypedArray(exemplar->GetElementsKind(),
length);
DCHECK_GE(new_array->length_value(), length);
// We don't need a validation step for one argument case since
// NewJSTypedArray always returns a non-neutered typed array.
} else if (argc == 3) {
Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(*argv[0]));
size_t byte_offset = NumberToSize(*argv[1]);
size_t length = NumberToSize(*argv[2]);
new_array = isolate->factory()->NewJSTypedArray(exemplar->type(), buffer,
byte_offset, length);
if (V8_UNLIKELY(new_array->WasNeutered())) {
const MessageTemplate::Template message =
MessageTemplate::kDetachedOperation;
Handle<String> operation =
isolate->factory()->NewStringFromAsciiChecked(method_name);
THROW_NEW_ERROR(isolate, NewTypeError(message, operation), JSTypedArray);
}
} else {
UNREACHABLE();
}
DCHECK(!new_array->WasNeutered());
return new_array;
}
// static
MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
Isolate* isolate, Handle<JSTypedArray> exemplar, int argc,
......@@ -16682,21 +16723,15 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
// slot.
DCHECK(exemplar->IsJSTypedArray());
// 2. Let defaultConstructor be the intrinsic object listed in column one of
// Table 51 for exemplar.[[TypedArrayName]].
Handle<JSFunction> default_ctor = isolate->uint8_array_fun();
switch (exemplar->type()) {
#define TYPED_ARRAY_CTOR(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: { \
default_ctor = isolate->type##_array_fun(); \
break; \
if (exemplar->HasJSTypedArrayPrototype(isolate) &&
isolate->IsArraySpeciesLookupChainIntact()) {
return CreateFast(isolate, exemplar, argc, argv, method_name);
}
TYPED_ARRAYS(TYPED_ARRAY_CTOR)
#undef TYPED_ARRAY_CTOR
default:
UNREACHABLE();
}
// 2. Let defaultConstructor be the intrinsic object listed in column one of
// Table 51 for exemplar.[[TypedArrayName]].
Handle<JSFunction> default_ctor =
JSTypedArray::DefaultConstructor(isolate, exemplar);
// 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
Handle<Object> ctor;
......@@ -16705,6 +16740,10 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
Object::SpeciesConstructor(isolate, exemplar, default_ctor),
JSTypedArray);
if (*default_ctor == *ctor) {
return CreateFast(isolate, exemplar, argc, argv, method_name);
}
// 4. Return ? TypedArrayCreate(constructor, argumentList).
return Create(isolate, ctor, argc, argv, method_name);
}
......
......@@ -204,6 +204,15 @@ void JSTypedArray::set_length(Object* value, WriteBarrierMode mode) {
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kLengthOffset, value, mode);
}
bool JSTypedArray::HasJSTypedArrayPrototype(Isolate* isolate) {
DisallowHeapAllocation no_gc;
Object* proto = map()->prototype();
if (!proto->IsJSObject()) return false;
JSObject* proto_obj = JSObject::cast(proto);
return proto_obj->map()->prototype() == *isolate->typed_array_prototype();
}
// static
MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate,
Handle<Object> receiver,
......@@ -227,6 +236,26 @@ MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate,
return array;
}
// static
Handle<JSFunction> JSTypedArray::DefaultConstructor(
Isolate* isolate, Handle<JSTypedArray> exemplar) {
Handle<JSFunction> default_ctor = isolate->uint8_array_fun();
switch (exemplar->type()) {
#define TYPED_ARRAY_CTOR(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: { \
default_ctor = isolate->type##_array_fun(); \
break; \
}
TYPED_ARRAYS(TYPED_ARRAY_CTOR)
#undef TYPED_ARRAY_CTOR
default:
UNREACHABLE();
}
return default_ctor;
}
#ifdef VERIFY_HEAP
ACCESSORS(JSTypedArray, raw_length, Object, kLengthOffset)
#endif
......
......@@ -299,14 +299,24 @@ class JSTypedArray : public JSArrayBufferView {
Handle<JSArrayBuffer> GetBuffer();
inline bool HasJSTypedArrayPrototype(Isolate* isolate);
static inline MaybeHandle<JSTypedArray> Validate(Isolate* isolate,
Handle<Object> receiver,
const char* method_name);
static inline Handle<JSFunction> DefaultConstructor(
Isolate* isolate, Handle<JSTypedArray> exemplar);
// ES7 section 22.2.4.6 Create ( constructor, argumentList )
static MaybeHandle<JSTypedArray> Create(Isolate* isolate,
Handle<Object> default_ctor, int argc,
Handle<Object>* argv,
const char* method_name);
// If there was no updates of constructors and species, SpeciesCreate invokes
// CreateFast instead of Create. This creates a typed array without lookup of
// species.
static MaybeHandle<JSTypedArray> CreateFast(Isolate* isolate,
Handle<JSTypedArray> exemplar,
int argc, Handle<Object>* argv,
const char* method_name);
// ES7 section 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList )
static MaybeHandle<JSTypedArray> SpeciesCreate(Isolate* isolate,
Handle<JSTypedArray> exemplar,
......
......@@ -85,5 +85,79 @@ TEST(AllocateNotExternal) {
CHECK_EQ(memory, buffer->GetContents().Data());
}
void TestSpeciesProtector(char* code,
bool invalidates_species_protector = true) {
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
std::string typed_array_constructors[] = {
#define TYPED_ARRAY_CTOR(Type, type, TYPE, ctype, size) #Type "Array",
TYPED_ARRAYS(TYPED_ARRAY_CTOR)
#undef TYPED_ARRAY_CTOR
};
for (auto& constructor : typed_array_constructors) {
v8::Isolate* isolate = v8::Isolate::New(create_params);
isolate->Enter();
{
LocalContext context(isolate);
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
CompileRun(("let x = new " + constructor + "();").c_str());
CompileRun(("let constructor = " + constructor + ";").c_str());
v8::Local<v8::Value> constructor_obj = CompileRun(constructor.c_str());
CHECK_EQ(constructor_obj, CompileRun("x.slice().constructor"));
CHECK_EQ(constructor_obj, CompileRun("x.map(()=>{}).constructor"));
std::string decl = "class MyTypedArray extends " + constructor + " { }";
CompileRun(decl.c_str());
v8::internal::Isolate* i_isolate =
reinterpret_cast<v8::internal::Isolate*>(isolate);
CHECK(i_isolate->IsArraySpeciesLookupChainIntact());
CompileRun(code);
if (invalidates_species_protector) {
CHECK(!i_isolate->IsArraySpeciesLookupChainIntact());
} else {
CHECK(i_isolate->IsArraySpeciesLookupChainIntact());
}
v8::Local<v8::Value> my_typed_array = CompileRun("MyTypedArray");
CHECK_EQ(my_typed_array, CompileRun("x.slice().constructor"));
CHECK_EQ(my_typed_array, CompileRun("x.map(()=>{}).constructor"));
}
isolate->Exit();
isolate->Dispose();
}
}
UNINITIALIZED_TEST(SpeciesConstructor) {
char code[] = "x.constructor = MyTypedArray";
TestSpeciesProtector(code);
}
UNINITIALIZED_TEST(SpeciesConstructorAccessor) {
char code[] =
"Object.defineProperty(x, 'constructor',{get() {return MyTypedArray;}})";
TestSpeciesProtector(code);
}
UNINITIALIZED_TEST(SpeciesModified) {
char code[] =
"Object.defineProperty(constructor, Symbol.species, "
"{value:MyTypedArray})";
TestSpeciesProtector(code);
}
UNINITIALIZED_TEST(SpeciesParentConstructor) {
char code[] = "constructor.prototype.constructor = MyTypedArray";
TestSpeciesProtector(code);
}
UNINITIALIZED_TEST(SpeciesProto) {
char code[] = "x.__proto__ = MyTypedArray.prototype";
TestSpeciesProtector(code, false);
}
} // namespace internal
} // namespace v8
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