[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:
jkummerow 2016-05-13 08:00:53 -07:00 committed by Commit bot
parent ced0f49763
commit 8313d2f0cb
2 changed files with 351 additions and 152 deletions

View File

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

View File

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