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() { ...@@ -237,22 +237,45 @@ void LookupIterator::ReloadPropertyInformation() {
DCHECK(IsFound() || !holder_->HasFastProperties()); 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() { void LookupIterator::InternalUpdateProtector() {
if (isolate_->bootstrapper()->IsActive()) return; if (isolate_->bootstrapper()->IsActive()) return;
if (*name_ == heap()->constructor_string()) { if (*name_ == heap()->constructor_string()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return; if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
// Setting the constructor property could change an instance's @@species // Setting the constructor property could change an instance's @@species
if (holder_->IsJSArray()) { if (holder_->IsJSArray() || holder_->IsJSTypedArray()) {
isolate_->CountUsage( isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified); v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
isolate_->InvalidateArraySpeciesProtector(); isolate_->InvalidateArraySpeciesProtector();
} else if (holder_->map()->is_prototype_map()) { } else if (holder_->map()->is_prototype_map()) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
// Setting the constructor of Array.prototype of any realm also needs // Setting the constructor of Array.prototype or %TypedArray%.prototype of
// to invalidate the species protector // 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_, 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:: isolate_->CountUsage(v8::Isolate::UseCounterFeature::
kArrayPrototypeConstructorModified); kArrayPrototypeConstructorModified);
isolate_->InvalidateArraySpeciesProtector(); isolate_->InvalidateArraySpeciesProtector();
...@@ -260,9 +283,10 @@ void LookupIterator::InternalUpdateProtector() { ...@@ -260,9 +283,10 @@ void LookupIterator::InternalUpdateProtector() {
} }
} else if (*name_ == heap()->species_symbol()) { } else if (*name_ == heap()->species_symbol()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return; if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
// Setting the Symbol.species property of any Array constructor invalidates // Setting the Symbol.species property of any Array or TypedArray
// the species protector // constructor invalidates the species protector
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) { if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX) ||
IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
isolate_->CountUsage( isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArraySpeciesModified); v8::Isolate::UseCounterFeature::kArraySpeciesModified);
isolate_->InvalidateArraySpeciesProtector(); isolate_->InvalidateArraySpeciesProtector();
......
...@@ -16674,6 +16674,47 @@ MaybeHandle<JSTypedArray> JSTypedArray::Create(Isolate* isolate, ...@@ -16674,6 +16674,47 @@ MaybeHandle<JSTypedArray> JSTypedArray::Create(Isolate* isolate,
return new_array; 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 // static
MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate( MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
Isolate* isolate, Handle<JSTypedArray> exemplar, int argc, Isolate* isolate, Handle<JSTypedArray> exemplar, int argc,
...@@ -16682,21 +16723,15 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate( ...@@ -16682,21 +16723,15 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
// slot. // slot.
DCHECK(exemplar->IsJSTypedArray()); DCHECK(exemplar->IsJSTypedArray());
// 2. Let defaultConstructor be the intrinsic object listed in column one of if (exemplar->HasJSTypedArrayPrototype(isolate) &&
// Table 51 for exemplar.[[TypedArrayName]]. isolate->IsArraySpeciesLookupChainIntact()) {
Handle<JSFunction> default_ctor = isolate->uint8_array_fun(); return CreateFast(isolate, exemplar, argc, argv, method_name);
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) // 2. Let defaultConstructor be the intrinsic object listed in column one of
#undef TYPED_ARRAY_CTOR // Table 51 for exemplar.[[TypedArrayName]].
default: Handle<JSFunction> default_ctor =
UNREACHABLE(); JSTypedArray::DefaultConstructor(isolate, exemplar);
}
// 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor). // 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
Handle<Object> ctor; Handle<Object> ctor;
...@@ -16705,6 +16740,10 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate( ...@@ -16705,6 +16740,10 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
Object::SpeciesConstructor(isolate, exemplar, default_ctor), Object::SpeciesConstructor(isolate, exemplar, default_ctor),
JSTypedArray); JSTypedArray);
if (*default_ctor == *ctor) {
return CreateFast(isolate, exemplar, argc, argv, method_name);
}
// 4. Return ? TypedArrayCreate(constructor, argumentList). // 4. Return ? TypedArrayCreate(constructor, argumentList).
return Create(isolate, ctor, argc, argv, method_name); return Create(isolate, ctor, argc, argv, method_name);
} }
......
...@@ -204,6 +204,15 @@ void JSTypedArray::set_length(Object* value, WriteBarrierMode mode) { ...@@ -204,6 +204,15 @@ void JSTypedArray::set_length(Object* value, WriteBarrierMode mode) {
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kLengthOffset, value, 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 // static
MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate, MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate,
Handle<Object> receiver, Handle<Object> receiver,
...@@ -227,6 +236,26 @@ MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate, ...@@ -227,6 +236,26 @@ MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate,
return array; 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 #ifdef VERIFY_HEAP
ACCESSORS(JSTypedArray, raw_length, Object, kLengthOffset) ACCESSORS(JSTypedArray, raw_length, Object, kLengthOffset)
#endif #endif
......
...@@ -299,14 +299,24 @@ class JSTypedArray : public JSArrayBufferView { ...@@ -299,14 +299,24 @@ class JSTypedArray : public JSArrayBufferView {
Handle<JSArrayBuffer> GetBuffer(); Handle<JSArrayBuffer> GetBuffer();
inline bool HasJSTypedArrayPrototype(Isolate* isolate);
static inline MaybeHandle<JSTypedArray> Validate(Isolate* isolate, static inline MaybeHandle<JSTypedArray> Validate(Isolate* isolate,
Handle<Object> receiver, Handle<Object> receiver,
const char* method_name); const char* method_name);
static inline Handle<JSFunction> DefaultConstructor(
Isolate* isolate, Handle<JSTypedArray> exemplar);
// ES7 section 22.2.4.6 Create ( constructor, argumentList ) // ES7 section 22.2.4.6 Create ( constructor, argumentList )
static MaybeHandle<JSTypedArray> Create(Isolate* isolate, static MaybeHandle<JSTypedArray> Create(Isolate* isolate,
Handle<Object> default_ctor, int argc, Handle<Object> default_ctor, int argc,
Handle<Object>* argv, Handle<Object>* argv,
const char* method_name); 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 ) // ES7 section 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList )
static MaybeHandle<JSTypedArray> SpeciesCreate(Isolate* isolate, static MaybeHandle<JSTypedArray> SpeciesCreate(Isolate* isolate,
Handle<JSTypedArray> exemplar, Handle<JSTypedArray> exemplar,
......
...@@ -85,5 +85,79 @@ TEST(AllocateNotExternal) { ...@@ -85,5 +85,79 @@ TEST(AllocateNotExternal) {
CHECK_EQ(memory, buffer->GetContents().Data()); 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 internal
} // namespace v8 } // 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