Reland "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() 2x faster in fast
cases.


Bug: chromium:800356, v8:7161
Change-Id: Ied8c90e23ca6708f4a3cec077c1fd733e4a6609e
Reviewed-on: https://chromium-review.googlesource.com/859397
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50617}
This commit is contained in:
Choongwoo Han 2018-01-15 19:26:57 +09:00 committed by Commit Bot
parent 1a0cc926ba
commit 3a4f3b73e2
5 changed files with 147 additions and 25 deletions

View File

@ -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();

View File

@ -16703,26 +16703,18 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
// 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; \
}
TYPED_ARRAYS(TYPED_ARRAY_CTOR)
#undef TYPED_ARRAY_CTOR
default:
UNREACHABLE();
}
Handle<JSFunction> default_ctor =
JSTypedArray::DefaultConstructor(isolate, exemplar);
// 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
Handle<Object> ctor;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, ctor,
Object::SpeciesConstructor(isolate, exemplar, default_ctor),
JSTypedArray);
Handle<Object> ctor = default_ctor;
if (!exemplar->HasJSTypedArrayPrototype(isolate) ||
!isolate->IsArraySpeciesLookupChainIntact()) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, ctor,
Object::SpeciesConstructor(isolate, exemplar, default_ctor),
JSTypedArray);
}
// 4. Return ? TypedArrayCreate(constructor, argumentList).
return Create(isolate, ctor, argc, argv, method_name);

View File

@ -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

View File

@ -299,9 +299,12 @@ 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,

View File

@ -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