[IC] Only cache handlers on a map that were compiled for that map
Globally cached handler stubs shouldn't be put into the on-map caches. This should speed up IC misses and save a bit of memory. Drive-by fix: transitioning StoreIC handlers were erroneously never cached. Review-Url: https://codereview.chromium.org/1974793002 Cr-Commit-Position: refs/heads/master@{#36247}
This commit is contained in:
parent
ced0f49763
commit
8313d2f0cb
496
src/ic/ic.cc
496
src/ic/ic.cc
@ -936,14 +936,28 @@ void IC::UpdateMegamorphicCache(Map* map, Name* name, Code* code) {
|
||||
|
||||
|
||||
Handle<Code> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> value) {
|
||||
// Try to find a globally shared handler stub.
|
||||
Handle<Code> code = GetMapIndependentHandler(lookup);
|
||||
if (!code.is_null()) return code;
|
||||
|
||||
// Otherwise check the map's handler cache for a map-specific handler, and
|
||||
// compile one if the cache comes up empty.
|
||||
bool receiver_is_holder =
|
||||
lookup->GetReceiver().is_identical_to(lookup->GetHolder<JSObject>());
|
||||
CacheHolderFlag flag;
|
||||
Handle<Map> stub_holder_map = IC::GetHandlerCacheHolder(
|
||||
receiver_map(), receiver_is_holder, isolate(), &flag);
|
||||
Handle<Map> stub_holder_map;
|
||||
if (kind() == Code::LOAD_IC || kind() == Code::KEYED_LOAD_IC) {
|
||||
stub_holder_map = IC::GetHandlerCacheHolder(
|
||||
receiver_map(), receiver_is_holder, isolate(), &flag);
|
||||
} else {
|
||||
DCHECK(kind() == Code::STORE_IC || kind() == Code::KEYED_STORE_IC);
|
||||
// Store handlers cannot be cached on prototypes.
|
||||
flag = kCacheOnReceiver;
|
||||
stub_holder_map = receiver_map();
|
||||
}
|
||||
|
||||
Handle<Code> code = PropertyHandlerCompiler::Find(
|
||||
lookup->name(), stub_holder_map, kind(), flag);
|
||||
code = PropertyHandlerCompiler::Find(lookup->name(), stub_holder_map, kind(),
|
||||
flag);
|
||||
// Use the cached value if it exists, and if it is different from the
|
||||
// handler that just missed.
|
||||
if (!code.is_null()) {
|
||||
@ -974,25 +988,13 @@ Handle<Code> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> value) {
|
||||
|
||||
code = CompileHandler(lookup, value, flag);
|
||||
DCHECK(code->is_handler());
|
||||
|
||||
// TODO(mvstanton): we'd only like to cache code on the map when it's custom
|
||||
// code compiled for this map, otherwise it's already cached in the global
|
||||
// code cache. We are also guarding against installing code with flags that
|
||||
// don't match the desired CacheHolderFlag computed above, which would lead to
|
||||
// invalid lookups later.
|
||||
bool is_normal = receiver_is_holder &&
|
||||
!lookup->GetHolder<JSReceiver>()->HasFastProperties();
|
||||
if (!is_normal && Code::ExtractCacheHolderFromFlags(code->flags()) == flag) {
|
||||
Map::UpdateCodeCache(stub_holder_map, lookup->name(), code);
|
||||
}
|
||||
DCHECK(Code::ExtractCacheHolderFromFlags(code->flags()) == flag);
|
||||
Map::UpdateCodeCache(stub_holder_map, lookup->name(), code);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
||||
Handle<Object> unused,
|
||||
CacheHolderFlag cache_holder) {
|
||||
Handle<Code> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
Handle<Object> receiver = lookup->GetReceiver();
|
||||
if (receiver->IsString() &&
|
||||
Name::Equals(isolate()->factory()->length_string(), lookup->name())) {
|
||||
@ -1024,17 +1026,8 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
||||
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||
bool receiver_is_holder = receiver.is_identical_to(holder);
|
||||
switch (lookup->state()) {
|
||||
case LookupIterator::INTERCEPTOR: {
|
||||
DCHECK(!holder->GetNamedInterceptor()->getter()->IsUndefined());
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptor);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
// Perform a lookup behind the interceptor. Copy the LookupIterator since
|
||||
// the original iterator will be used to fetch the value.
|
||||
LookupIterator it = *lookup;
|
||||
it.Next();
|
||||
LookupForRead(&it);
|
||||
return compiler.CompileLoadInterceptor(&it);
|
||||
}
|
||||
case LookupIterator::INTERCEPTOR:
|
||||
break; // Custom-compiled handler.
|
||||
|
||||
case LookupIterator::ACCESSOR: {
|
||||
// Use simple field loads for some well-known callback properties.
|
||||
@ -1057,73 +1050,59 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
||||
if (IsCompatibleReceiver(lookup, map)) {
|
||||
Handle<Object> accessors = lookup->GetAccessors();
|
||||
if (accessors->IsAccessorPair()) {
|
||||
if (!holder->HasFastProperties()) break;
|
||||
// When debugging we need to go the slow path to flood the accessor.
|
||||
if (GetSharedFunctionInfo()->HasDebugInfo()) break;
|
||||
Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
|
||||
isolate());
|
||||
CallOptimization call_optimization(getter);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder,
|
||||
cache_holder);
|
||||
if (call_optimization.is_simple_api_call()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadCallback);
|
||||
int index = lookup->GetAccessorIndex();
|
||||
return compiler.CompileLoadCallback(lookup->name(),
|
||||
call_optimization, index);
|
||||
if (!holder->HasFastProperties()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadViaGetter);
|
||||
int expected_arguments = Handle<JSFunction>::cast(getter)
|
||||
->shared()
|
||||
->internal_formal_parameter_count();
|
||||
return compiler.CompileLoadViaGetter(
|
||||
lookup->name(), lookup->GetAccessorIndex(), expected_arguments);
|
||||
// When debugging we need to go the slow path to flood the accessor.
|
||||
if (GetSharedFunctionInfo()->HasDebugInfo()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
break; // Custom-compiled handler.
|
||||
} else if (accessors->IsAccessorInfo()) {
|
||||
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
|
||||
if (v8::ToCData<Address>(info->getter()) == nullptr) break;
|
||||
if (!AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map)) {
|
||||
// This case should be already handled in LoadIC::UpdateCaches.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
if (v8::ToCData<Address>(info->getter()) == nullptr) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
if (!holder->HasFastProperties()) break;
|
||||
// Ruled out by IsCompatibleReceiver() above.
|
||||
DCHECK(AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map));
|
||||
if (!holder->HasFastProperties()) return slow_stub();
|
||||
if (receiver_is_holder) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterStub);
|
||||
int index = lookup->GetAccessorIndex();
|
||||
LoadApiGetterStub stub(isolate(), true, index);
|
||||
return stub.GetCode();
|
||||
}
|
||||
if (info->is_sloppy() && !receiver->IsJSReceiver()) break;
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadCallback);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder,
|
||||
cache_holder);
|
||||
return compiler.CompileLoadCallback(lookup->name(), info);
|
||||
if (info->is_sloppy() && !receiver->IsJSReceiver()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
}
|
||||
break;
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
|
||||
case LookupIterator::DATA: {
|
||||
if (lookup->is_dictionary_holder()) {
|
||||
if (kind() != Code::LOAD_IC) break;
|
||||
if (kind() != Code::LOAD_IC) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
if (holder->IsJSGlobalObject()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadGlobal);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder,
|
||||
cache_holder);
|
||||
Handle<PropertyCell> cell = lookup->GetPropertyCell();
|
||||
Handle<Code> code = compiler.CompileLoadGlobal(
|
||||
cell, lookup->name(), lookup->IsConfigurable());
|
||||
// TODO(verwaest): Move caching of these NORMAL stubs outside as well.
|
||||
CacheHolderFlag flag;
|
||||
Handle<Map> stub_holder_map =
|
||||
GetHandlerCacheHolder(map, receiver_is_holder, isolate(), &flag);
|
||||
Map::UpdateCodeCache(stub_holder_map, lookup->name(), code);
|
||||
return code;
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
// There is only one shared stub for loading normalized
|
||||
// properties. It does not traverse the prototype chain, so the
|
||||
// property must be found in the object for the stub to be
|
||||
// applicable.
|
||||
if (!receiver_is_holder) break;
|
||||
if (!receiver_is_holder) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormal);
|
||||
return isolate()->builtins()->LoadIC_Normal();
|
||||
}
|
||||
@ -1134,9 +1113,7 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
||||
if (receiver_is_holder) {
|
||||
return SimpleFieldLoad(field);
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadField);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
return compiler.CompileLoadField(lookup->name(), field);
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
|
||||
// -------------- Constant properties --------------
|
||||
@ -1146,14 +1123,12 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
||||
LoadConstantStub stub(isolate(), lookup->GetConstantIndex());
|
||||
return stub.GetCode();
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstant);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
return compiler.CompileLoadConstant(lookup->name(),
|
||||
lookup->GetConstantIndex());
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
|
||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||
break;
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return slow_stub();
|
||||
case LookupIterator::ACCESS_CHECK:
|
||||
case LookupIterator::JSPROXY:
|
||||
case LookupIterator::NOT_FOUND:
|
||||
@ -1161,7 +1136,129 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
||||
Handle<Object> unused,
|
||||
CacheHolderFlag cache_holder) {
|
||||
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||
#ifdef DEBUG
|
||||
// Only used by DCHECKs below.
|
||||
Handle<Object> receiver = lookup->GetReceiver();
|
||||
bool receiver_is_holder = receiver.is_identical_to(holder);
|
||||
#endif
|
||||
// Non-map-specific handler stubs have already been selected.
|
||||
DCHECK(!receiver->IsString() ||
|
||||
!Name::Equals(isolate()->factory()->length_string(), lookup->name()));
|
||||
DCHECK(!receiver->IsStringWrapper() ||
|
||||
!Name::Equals(isolate()->factory()->length_string(), lookup->name()));
|
||||
|
||||
DCHECK(!(
|
||||
receiver->IsJSFunction() &&
|
||||
Name::Equals(isolate()->factory()->prototype_string(), lookup->name()) &&
|
||||
receiver->IsConstructor() &&
|
||||
!Handle<JSFunction>::cast(receiver)
|
||||
->map()
|
||||
->has_non_instance_prototype()));
|
||||
|
||||
Handle<Map> map = receiver_map();
|
||||
switch (lookup->state()) {
|
||||
case LookupIterator::INTERCEPTOR: {
|
||||
DCHECK(!holder->GetNamedInterceptor()->getter()->IsUndefined());
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptor);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
// Perform a lookup behind the interceptor. Copy the LookupIterator since
|
||||
// the original iterator will be used to fetch the value.
|
||||
LookupIterator it = *lookup;
|
||||
it.Next();
|
||||
LookupForRead(&it);
|
||||
return compiler.CompileLoadInterceptor(&it);
|
||||
}
|
||||
|
||||
case LookupIterator::ACCESSOR: {
|
||||
#ifdef DEBUG
|
||||
int object_offset;
|
||||
DCHECK(!Accessors::IsJSObjectFieldAccessor(map, lookup->name(),
|
||||
&object_offset));
|
||||
DCHECK(!Accessors::IsJSArrayBufferViewFieldAccessor(map, lookup->name(),
|
||||
&object_offset));
|
||||
#endif
|
||||
|
||||
DCHECK(IsCompatibleReceiver(lookup, map));
|
||||
Handle<Object> accessors = lookup->GetAccessors();
|
||||
if (accessors->IsAccessorPair()) {
|
||||
DCHECK(holder->HasFastProperties());
|
||||
DCHECK(!GetSharedFunctionInfo()->HasDebugInfo());
|
||||
Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
|
||||
isolate());
|
||||
CallOptimization call_optimization(getter);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
if (call_optimization.is_simple_api_call()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadCallback);
|
||||
int index = lookup->GetAccessorIndex();
|
||||
return compiler.CompileLoadCallback(lookup->name(), call_optimization,
|
||||
index);
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadViaGetter);
|
||||
int expected_arguments = Handle<JSFunction>::cast(getter)
|
||||
->shared()
|
||||
->internal_formal_parameter_count();
|
||||
return compiler.CompileLoadViaGetter(
|
||||
lookup->name(), lookup->GetAccessorIndex(), expected_arguments);
|
||||
} else {
|
||||
DCHECK(accessors->IsAccessorInfo());
|
||||
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
|
||||
DCHECK(v8::ToCData<Address>(info->getter()) != nullptr);
|
||||
DCHECK(AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map));
|
||||
DCHECK(holder->HasFastProperties());
|
||||
DCHECK(!receiver_is_holder);
|
||||
DCHECK(!info->is_sloppy() || receiver->IsJSReceiver());
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadCallback);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
return compiler.CompileLoadCallback(lookup->name(), info);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
case LookupIterator::DATA: {
|
||||
if (lookup->is_dictionary_holder()) {
|
||||
DCHECK(kind() == Code::LOAD_IC);
|
||||
DCHECK(holder->IsJSGlobalObject());
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadGlobal);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
Handle<PropertyCell> cell = lookup->GetPropertyCell();
|
||||
Handle<Code> code = compiler.CompileLoadGlobal(
|
||||
cell, lookup->name(), lookup->IsConfigurable());
|
||||
return code;
|
||||
}
|
||||
|
||||
// -------------- Fields --------------
|
||||
if (lookup->property_details().type() == DATA) {
|
||||
FieldIndex field = lookup->GetFieldIndex();
|
||||
DCHECK(!receiver_is_holder);
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadField);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
return compiler.CompileLoadField(lookup->name(), field);
|
||||
}
|
||||
|
||||
// -------------- Constant properties --------------
|
||||
DCHECK(lookup->property_details().type() == DATA_CONSTANT);
|
||||
DCHECK(!receiver_is_holder);
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstant);
|
||||
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
|
||||
return compiler.CompileLoadConstant(lookup->name(),
|
||||
lookup->GetConstantIndex());
|
||||
}
|
||||
|
||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||
case LookupIterator::ACCESS_CHECK:
|
||||
case LookupIterator::JSPROXY:
|
||||
case LookupIterator::NOT_FOUND:
|
||||
case LookupIterator::TRANSITION:
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return slow_stub();
|
||||
}
|
||||
|
||||
@ -1513,6 +1610,135 @@ static Handle<Code> PropertyCellStoreHandler(
|
||||
return code;
|
||||
}
|
||||
|
||||
Handle<Code> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
DCHECK_NE(LookupIterator::JSPROXY, lookup->state());
|
||||
|
||||
// This is currently guaranteed by checks in StoreIC::Store.
|
||||
Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver());
|
||||
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||
DCHECK(!receiver->IsAccessCheckNeeded() || lookup->name()->IsPrivate());
|
||||
|
||||
switch (lookup->state()) {
|
||||
case LookupIterator::TRANSITION: {
|
||||
auto store_target = lookup->GetStoreTarget();
|
||||
if (store_target->IsJSGlobalObject()) {
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
// Currently not handled by CompileStoreTransition.
|
||||
if (!holder->HasFastProperties()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "transition from slow");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
|
||||
DCHECK(lookup->IsCacheableTransition());
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
|
||||
case LookupIterator::INTERCEPTOR: {
|
||||
DCHECK(!holder->GetNamedInterceptor()->setter()->IsUndefined());
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreInterceptorStub);
|
||||
StoreInterceptorStub stub(isolate());
|
||||
return stub.GetCode();
|
||||
}
|
||||
|
||||
case LookupIterator::ACCESSOR: {
|
||||
if (!holder->HasFastProperties()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "accessor on slow map");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
Handle<Object> accessors = lookup->GetAccessors();
|
||||
if (accessors->IsAccessorInfo()) {
|
||||
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
|
||||
if (v8::ToCData<Address>(info->setter()) == nullptr) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "setter == nullptr");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
if (AccessorInfo::cast(*accessors)->is_special_data_property() &&
|
||||
!lookup->HolderIsReceiverOrHiddenPrototype()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC",
|
||||
"special data property in prototype chain");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
if (!AccessorInfo::IsCompatibleReceiverMap(isolate(), info,
|
||||
receiver_map())) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "incompatible receiver type");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
if (info->is_sloppy() && !receiver->IsJSReceiver()) {
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
break; // Custom-compiled handler.
|
||||
} else if (accessors->IsAccessorPair()) {
|
||||
Handle<Object> setter(Handle<AccessorPair>::cast(accessors)->setter(),
|
||||
isolate());
|
||||
if (!setter->IsJSFunction() && !setter->IsFunctionTemplateInfo()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "setter not a function");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
CallOptimization call_optimization(setter);
|
||||
if (call_optimization.is_simple_api_call()) {
|
||||
if (call_optimization.IsCompatibleReceiver(receiver, holder)) {
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "incompatible receiver");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
|
||||
case LookupIterator::DATA: {
|
||||
if (lookup->is_dictionary_holder()) {
|
||||
if (holder->IsJSGlobalObject()) {
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreNormal);
|
||||
DCHECK(holder.is_identical_to(receiver));
|
||||
return isolate()->builtins()->StoreIC_Normal();
|
||||
}
|
||||
|
||||
// -------------- Fields --------------
|
||||
if (lookup->property_details().type() == DATA) {
|
||||
bool use_stub = true;
|
||||
if (lookup->representation().IsHeapObject()) {
|
||||
// Only use a generic stub if no types need to be tracked.
|
||||
Handle<FieldType> field_type = lookup->GetFieldType();
|
||||
use_stub = !field_type->IsClass();
|
||||
}
|
||||
if (use_stub) {
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreFieldStub);
|
||||
StoreFieldStub stub(isolate(), lookup->GetFieldIndex(),
|
||||
lookup->representation());
|
||||
return stub.GetCode();
|
||||
}
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
|
||||
// -------------- Constant properties --------------
|
||||
DCHECK(lookup->property_details().type() == DATA_CONSTANT);
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "constant property");
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
|
||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||
case LookupIterator::ACCESS_CHECK:
|
||||
case LookupIterator::JSPROXY:
|
||||
case LookupIterator::NOT_FOUND:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
|
||||
Handle<Object> value,
|
||||
@ -1540,10 +1766,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
|
||||
}
|
||||
Handle<Map> transition = lookup->transition_map();
|
||||
// Currently not handled by CompileStoreTransition.
|
||||
if (!holder->HasFastProperties()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "transition from slow");
|
||||
break;
|
||||
}
|
||||
DCHECK(holder->HasFastProperties());
|
||||
|
||||
DCHECK(lookup->IsCacheableTransition());
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreTransition);
|
||||
@ -1551,59 +1774,37 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
|
||||
return compiler.CompileStoreTransition(transition, lookup->name());
|
||||
}
|
||||
|
||||
case LookupIterator::INTERCEPTOR: {
|
||||
DCHECK(!holder->GetNamedInterceptor()->setter()->IsUndefined());
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreInterceptorStub);
|
||||
StoreInterceptorStub stub(isolate());
|
||||
return stub.GetCode();
|
||||
}
|
||||
case LookupIterator::INTERCEPTOR:
|
||||
UNREACHABLE();
|
||||
|
||||
case LookupIterator::ACCESSOR: {
|
||||
if (!holder->HasFastProperties()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "accessor on slow map");
|
||||
break;
|
||||
}
|
||||
DCHECK(holder->HasFastProperties());
|
||||
Handle<Object> accessors = lookup->GetAccessors();
|
||||
if (accessors->IsAccessorInfo()) {
|
||||
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
|
||||
if (v8::ToCData<Address>(info->setter()) == 0) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "setter == 0");
|
||||
break;
|
||||
}
|
||||
if (AccessorInfo::cast(*accessors)->is_special_data_property() &&
|
||||
!lookup->HolderIsReceiverOrHiddenPrototype()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC",
|
||||
"special data property in prototype chain");
|
||||
break;
|
||||
}
|
||||
if (!AccessorInfo::IsCompatibleReceiverMap(isolate(), info,
|
||||
receiver_map())) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "incompatible receiver type");
|
||||
break;
|
||||
}
|
||||
if (info->is_sloppy() && !receiver->IsJSReceiver()) break;
|
||||
DCHECK(v8::ToCData<Address>(info->setter()) != 0);
|
||||
DCHECK(!AccessorInfo::cast(*accessors)->is_special_data_property() ||
|
||||
lookup->HolderIsReceiverOrHiddenPrototype());
|
||||
DCHECK(AccessorInfo::IsCompatibleReceiverMap(isolate(), info,
|
||||
receiver_map()));
|
||||
DCHECK(!info->is_sloppy() || receiver->IsJSReceiver());
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreCallback);
|
||||
NamedStoreHandlerCompiler compiler(isolate(), receiver_map(), holder);
|
||||
return compiler.CompileStoreCallback(receiver, lookup->name(), info,
|
||||
language_mode());
|
||||
} else if (accessors->IsAccessorPair()) {
|
||||
} else {
|
||||
DCHECK(accessors->IsAccessorPair());
|
||||
Handle<Object> setter(Handle<AccessorPair>::cast(accessors)->setter(),
|
||||
isolate());
|
||||
if (!setter->IsJSFunction() && !setter->IsFunctionTemplateInfo()) {
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "setter not a function");
|
||||
break;
|
||||
}
|
||||
DCHECK(setter->IsJSFunction() || setter->IsFunctionTemplateInfo());
|
||||
CallOptimization call_optimization(setter);
|
||||
NamedStoreHandlerCompiler compiler(isolate(), receiver_map(), holder);
|
||||
if (call_optimization.is_simple_api_call()) {
|
||||
if (call_optimization.IsCompatibleReceiver(receiver, holder)) {
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreCallback);
|
||||
return compiler.CompileStoreCallback(receiver, lookup->name(),
|
||||
call_optimization,
|
||||
lookup->GetAccessorIndex());
|
||||
}
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "incompatible receiver");
|
||||
break;
|
||||
DCHECK(call_optimization.IsCompatibleReceiver(receiver, holder));
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreCallback);
|
||||
return compiler.CompileStoreCallback(receiver, lookup->name(),
|
||||
call_optimization,
|
||||
lookup->GetAccessorIndex());
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreViaSetter);
|
||||
int expected_arguments = JSFunction::cast(*setter)
|
||||
@ -1613,42 +1814,34 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
|
||||
lookup->GetAccessorIndex(),
|
||||
expected_arguments);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LookupIterator::DATA: {
|
||||
if (lookup->is_dictionary_holder()) {
|
||||
if (holder->IsJSGlobalObject()) {
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobal);
|
||||
DCHECK(holder.is_identical_to(receiver) ||
|
||||
receiver->map()->prototype() == *holder);
|
||||
auto cell = lookup->GetPropertyCell();
|
||||
auto updated_type = PropertyCell::UpdatedType(
|
||||
cell, value, lookup->property_details());
|
||||
auto code = PropertyCellStoreHandler(
|
||||
isolate(), receiver, Handle<JSGlobalObject>::cast(holder),
|
||||
lookup->name(), cell, updated_type);
|
||||
return code;
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreNormal);
|
||||
DCHECK(holder.is_identical_to(receiver));
|
||||
return isolate()->builtins()->StoreIC_Normal();
|
||||
DCHECK(holder->IsJSGlobalObject());
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobal);
|
||||
DCHECK(holder.is_identical_to(receiver) ||
|
||||
receiver->map()->prototype() == *holder);
|
||||
auto cell = lookup->GetPropertyCell();
|
||||
auto updated_type =
|
||||
PropertyCell::UpdatedType(cell, value, lookup->property_details());
|
||||
auto code = PropertyCellStoreHandler(
|
||||
isolate(), receiver, Handle<JSGlobalObject>::cast(holder),
|
||||
lookup->name(), cell, updated_type);
|
||||
return code;
|
||||
}
|
||||
|
||||
// -------------- Fields --------------
|
||||
if (lookup->property_details().type() == DATA) {
|
||||
#ifdef DEBUG
|
||||
bool use_stub = true;
|
||||
if (lookup->representation().IsHeapObject()) {
|
||||
// Only use a generic stub if no types need to be tracked.
|
||||
Handle<FieldType> field_type = lookup->GetFieldType();
|
||||
use_stub = !field_type->IsClass();
|
||||
}
|
||||
if (use_stub) {
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreFieldStub);
|
||||
StoreFieldStub stub(isolate(), lookup->GetFieldIndex(),
|
||||
lookup->representation());
|
||||
return stub.GetCode();
|
||||
}
|
||||
DCHECK(!use_stub);
|
||||
#endif
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreField);
|
||||
NamedStoreHandlerCompiler compiler(isolate(), receiver_map(), holder);
|
||||
return compiler.CompileStoreField(lookup);
|
||||
@ -1656,8 +1849,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
|
||||
|
||||
// -------------- Constant properties --------------
|
||||
DCHECK(lookup->property_details().type() == DATA_CONSTANT);
|
||||
TRACE_GENERIC_IC(isolate(), "StoreIC", "constant property");
|
||||
break;
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||
@ -1666,7 +1858,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
|
||||
case LookupIterator::NOT_FOUND:
|
||||
UNREACHABLE();
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
UNREACHABLE();
|
||||
return slow_stub();
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,10 @@ class IC {
|
||||
// Compute the handler either by compiling or by retrieving a cached version.
|
||||
Handle<Code> ComputeHandler(LookupIterator* lookup,
|
||||
Handle<Object> value = Handle<Code>::null());
|
||||
virtual Handle<Code> GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
virtual Handle<Code> CompileHandler(LookupIterator* lookup,
|
||||
Handle<Object> value,
|
||||
CacheHolderFlag cache_holder) {
|
||||
@ -304,6 +308,8 @@ class LoadIC : public IC {
|
||||
// lookup result.
|
||||
void UpdateCaches(LookupIterator* lookup);
|
||||
|
||||
Handle<Code> GetMapIndependentHandler(LookupIterator* lookup) override;
|
||||
|
||||
Handle<Code> CompileHandler(LookupIterator* lookup, Handle<Object> unused,
|
||||
CacheHolderFlag cache_holder) override;
|
||||
|
||||
@ -386,6 +392,7 @@ class StoreIC : public IC {
|
||||
// lookup result.
|
||||
void UpdateCaches(LookupIterator* lookup, Handle<Object> value,
|
||||
JSReceiver::StoreFromKeyed store_mode);
|
||||
Handle<Code> GetMapIndependentHandler(LookupIterator* lookup) override;
|
||||
Handle<Code> CompileHandler(LookupIterator* lookup, Handle<Object> value,
|
||||
CacheHolderFlag cache_holder) override;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user