Commit c97535f5 authored by ishell's avatar ishell Committed by Commit bot

[ic] Support non-code handlers in megamorphic stub cache.

BUG=

Review-Url: https://codereview.chromium.org/2412043003
Cr-Commit-Position: refs/heads/master@{#40255}
parent 77bda187
......@@ -85,29 +85,6 @@ inline int FieldIndex::GetLoadByFieldIndex() const {
return is_double() ? (result | 1) : result;
}
// Takes an offset as computed by GetLoadByFieldOffset and reconstructs a
// FieldIndex object from it.
// static
inline FieldIndex FieldIndex::ForLoadByFieldOffset(Map* map, int offset) {
DCHECK(LoadHandlerTypeBit::decode(offset) == kLoadICHandlerForProperties);
bool is_inobject = FieldOffsetIsInobject::decode(offset);
bool is_double = FieldOffsetIsDouble::decode(offset);
int field_index = FieldOffsetOffset::decode(offset) >> kPointerSizeLog2;
int first_inobject_offset = 0;
if (is_inobject) {
first_inobject_offset =
map->IsJSObjectMap() ? map->GetInObjectPropertyOffset(0) : 0;
} else {
first_inobject_offset = FixedArray::kHeaderSize;
}
int inobject_properties =
map->IsJSObjectMap() ? map->GetInObjectProperties() : 0;
FieldIndex result(is_inobject, field_index, is_double, inobject_properties,
first_inobject_offset);
DCHECK(result.GetLoadByFieldOffset() == offset);
return result;
}
// Returns the offset format consumed by TurboFan stubs:
// (offset << 3) | (is_double << 2) | (is_inobject << 1) | is_property
// Where |offset| is relative to object start or FixedArray start, respectively.
......
......@@ -27,7 +27,6 @@ class FieldIndex final {
static FieldIndex ForInObjectOffset(int offset, Map* map = NULL);
static FieldIndex ForDescriptor(Map* map, int descriptor_index);
static FieldIndex ForLoadByFieldIndex(Map* map, int index);
static FieldIndex ForLoadByFieldOffset(Map* map, int index);
static FieldIndex ForKeyedLookupCacheIndex(Map* map, int index);
static FieldIndex FromFieldAccessStubKey(int key);
......
......@@ -92,6 +92,11 @@ Code* IC::target() const {
return GetTargetAtAddress(address(), constant_pool());
}
bool IC::IsHandler(Object* object) {
return (object->IsSmi() && (object != nullptr)) ||
(object->IsCode() && Code::cast(object)->is_handler());
}
Handle<Map> IC::GetHandlerCacheHolder(Handle<Map> receiver_map,
bool receiver_is_holder, Isolate* isolate,
CacheHolderFlag* flag) {
......
......@@ -691,11 +691,8 @@ static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps,
return true;
}
bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> code) {
DCHECK(code->IsSmi() || code->IsCode());
if (!code->IsSmi() && !Code::cast(*code)->is_handler()) {
return false;
}
bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> handler) {
DCHECK(IsHandler(*handler));
if (is_keyed() && state() != RECOMPUTE_HANDLER) return false;
Handle<Map> map = receiver_map();
MapHandleList maps;
......@@ -735,16 +732,16 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> code) {
number_of_valid_maps++;
if (number_of_valid_maps > 1 && is_keyed()) return false;
if (number_of_valid_maps == 1) {
ConfigureVectorState(name, receiver_map(), code);
ConfigureVectorState(name, receiver_map(), handler);
} else {
if (handler_to_overwrite >= 0) {
handlers.Set(handler_to_overwrite, code);
handlers.Set(handler_to_overwrite, handler);
if (!map.is_identical_to(maps.at(handler_to_overwrite))) {
maps.Set(handler_to_overwrite, map);
}
} else {
maps.Add(map);
handlers.Add(code);
handlers.Add(handler);
}
ConfigureVectorState(name, &maps, &handlers);
......@@ -754,8 +751,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> code) {
}
void IC::UpdateMonomorphicIC(Handle<Object> handler, Handle<Name> name) {
DCHECK(handler->IsSmi() ||
(handler->IsCode() && Handle<Code>::cast(handler)->is_handler()));
DCHECK(IsHandler(*handler));
ConfigureVectorState(name, receiver_map(), handler);
}
......@@ -786,24 +782,26 @@ bool IC::IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map) {
return transitioned_map == target_map;
}
void IC::PatchCache(Handle<Name> name, Handle<Object> code) {
DCHECK(code->IsCode() || (code->IsSmi() && (kind() == Code::LOAD_IC ||
kind() == Code::KEYED_LOAD_IC)));
void IC::PatchCache(Handle<Name> name, Handle<Object> handler) {
DCHECK(IsHandler(*handler));
// Currently only LoadIC and KeyedLoadIC support non-code handlers.
DCHECK_IMPLIES(!handler->IsCode(),
kind() == Code::LOAD_IC || kind() == Code::KEYED_LOAD_IC);
switch (state()) {
case UNINITIALIZED:
case PREMONOMORPHIC:
UpdateMonomorphicIC(code, name);
UpdateMonomorphicIC(handler, name);
break;
case RECOMPUTE_HANDLER:
case MONOMORPHIC:
if (kind() == Code::LOAD_GLOBAL_IC) {
UpdateMonomorphicIC(code, name);
UpdateMonomorphicIC(handler, name);
break;
}
// Fall through.
case POLYMORPHIC:
if (!is_keyed() || state() == RECOMPUTE_HANDLER) {
if (UpdatePolymorphicIC(name, code)) break;
if (UpdatePolymorphicIC(name, handler)) break;
// For keyed stubs, we can't know whether old handlers were for the
// same key.
CopyICToMegamorphicCache(name);
......@@ -812,7 +810,7 @@ void IC::PatchCache(Handle<Name> name, Handle<Object> code) {
ConfigureVectorState(MEGAMORPHIC, name);
// Fall through.
case MEGAMORPHIC:
UpdateMegamorphicCache(*receiver_map(), *name, *code);
UpdateMegamorphicCache(*receiver_map(), *name, *handler);
// Indicate that we've handled this case.
DCHECK(UseVector());
vector_set_ = true;
......@@ -964,30 +962,17 @@ StubCache* IC::stub_cache() {
return nullptr;
}
void IC::UpdateMegamorphicCache(Map* map, Name* name, Object* code) {
if (code->IsSmi()) {
// TODO(jkummerow): Support Smis in the code cache.
Handle<Map> map_handle(map, isolate());
Handle<Name> name_handle(name, isolate());
FieldIndex index =
FieldIndex::ForLoadByFieldOffset(map, Smi::cast(code)->value());
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldStub);
LoadFieldStub stub(isolate(), index);
Code* handler = *stub.GetCode();
stub_cache()->Set(*name_handle, *map_handle, handler);
return;
}
DCHECK(code->IsCode());
stub_cache()->Set(name, map, Code::cast(code));
void IC::UpdateMegamorphicCache(Map* map, Name* name, Object* handler) {
stub_cache()->Set(name, map, handler);
}
Handle<Object> IC::ComputeHandler(LookupIterator* lookup,
Handle<Object> value) {
// Try to find a globally shared handler stub.
Handle<Object> handler_or_index = GetMapIndependentHandler(lookup);
if (!handler_or_index.is_null()) {
DCHECK(handler_or_index->IsCode() || handler_or_index->IsSmi());
return handler_or_index;
Handle<Object> handler = GetMapIndependentHandler(lookup);
if (!handler.is_null()) {
DCHECK(IC::IsHandler(*handler));
return handler;
}
// Otherwise check the map's handler cache for a map-specific handler, and
......@@ -1024,8 +1009,9 @@ Handle<Object> IC::ComputeHandler(LookupIterator* lookup,
// cache (which just missed) is different from the cached handler.
if (state() == MEGAMORPHIC && lookup->GetReceiver()->IsHeapObject()) {
Map* map = Handle<HeapObject>::cast(lookup->GetReceiver())->map();
Code* megamorphic_cached_code = stub_cache()->Get(*lookup->name(), map);
if (megamorphic_cached_code != *code) {
Object* megamorphic_cached_handler =
stub_cache()->Get(*lookup->name(), map);
if (megamorphic_cached_handler != *code) {
TRACE_HANDLER_STATS(isolate(), IC_HandlerCacheHit);
return code;
}
......
......@@ -81,6 +81,8 @@ class IC {
static InlineCacheState StateFromCode(Code* code);
static inline bool IsHandler(Object* object);
protected:
Address fp() const { return fp_; }
Address pc() const { return *pc_address_; }
......
......@@ -6,13 +6,18 @@
#include "src/ast/ast.h"
#include "src/base/bits.h"
#include "src/ic/ic-inl.h"
#include "src/type-info.h"
namespace v8 {
namespace internal {
StubCache::StubCache(Isolate* isolate, Code::Kind ic_kind)
: isolate_(isolate), ic_kind_(ic_kind) {}
: isolate_(isolate), ic_kind_(ic_kind) {
// Ensure the nullptr (aka Smi::kZero) which StubCache::Get() returns
// when the entry is not found is not considered as a handler.
DCHECK(!IC::IsHandler(nullptr));
}
void StubCache::Initialize() {
DCHECK(base::bits::IsPowerOfTwo32(kPrimaryTableSize));
......@@ -24,18 +29,22 @@ void StubCache::Initialize() {
namespace {
bool CommonStubCacheChecks(StubCache* stub_cache, Name* name, Map* map,
Code* code) {
Object* handler) {
// Validate that the name does not move on scavenge, and that we
// can use identity checks instead of structural equality checks.
DCHECK(!name->GetHeap()->InNewSpace(name));
DCHECK(name->IsUniqueName());
DCHECK(name->HasHashCode());
if (code) {
Code::Flags expected_flags = Code::RemoveHolderFromFlags(
Code::ComputeHandlerFlags(stub_cache->ic_kind()));
Code::Flags flags = Code::RemoveHolderFromFlags(code->flags());
DCHECK_EQ(expected_flags, flags);
DCHECK_EQ(Code::HANDLER, Code::ExtractKindFromFlags(code->flags()));
if (handler) {
DCHECK(IC::IsHandler(handler));
if (handler->IsCode()) {
Code* code = Code::cast(handler);
Code::Flags expected_flags = Code::RemoveHolderFromFlags(
Code::ComputeHandlerFlags(stub_cache->ic_kind()));
Code::Flags flags = Code::RemoveHolderFromFlags(code->flags());
DCHECK_EQ(expected_flags, flags);
DCHECK_EQ(Code::HANDLER, Code::ExtractKindFromFlags(code->flags()));
}
}
return true;
}
......@@ -43,17 +52,17 @@ bool CommonStubCacheChecks(StubCache* stub_cache, Name* name, Map* map,
} // namespace
#endif
Code* StubCache::Set(Name* name, Map* map, Code* code) {
DCHECK(CommonStubCacheChecks(this, name, map, code));
Object* StubCache::Set(Name* name, Map* map, Object* handler) {
DCHECK(CommonStubCacheChecks(this, name, map, handler));
// Compute the primary entry.
int primary_offset = PrimaryOffset(name, map);
Entry* primary = entry(primary_, primary_offset);
Code* old_code = primary->value;
Object* old_handler = primary->value;
// If the primary entry has useful data in it, we retire it to the
// secondary cache before overwriting it.
if (old_code != isolate_->builtins()->builtin(Builtins::kIllegal)) {
if (old_handler != isolate_->builtins()->builtin(Builtins::kIllegal)) {
Map* old_map = primary->map;
int seed = PrimaryOffset(primary->key, old_map);
int secondary_offset = SecondaryOffset(primary->key, seed);
......@@ -63,13 +72,13 @@ Code* StubCache::Set(Name* name, Map* map, Code* code) {
// Update primary cache.
primary->key = name;
primary->value = code;
primary->value = handler;
primary->map = map;
isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
return code;
return handler;
}
Code* StubCache::Get(Name* name, Map* map) {
Object* StubCache::Get(Name* name, Map* map) {
DCHECK(CommonStubCacheChecks(this, name, map, nullptr));
int primary_offset = PrimaryOffset(name, map);
Entry* primary = entry(primary_, primary_offset);
......@@ -81,7 +90,7 @@ Code* StubCache::Get(Name* name, Map* map) {
if (secondary->key == name && secondary->map == map) {
return secondary->value;
}
return NULL;
return nullptr;
}
......
......@@ -35,14 +35,14 @@ class StubCache {
public:
struct Entry {
Name* key;
Code* value;
Object* value;
Map* map;
};
void Initialize();
// Access cache for entry hash(name, map).
Code* Set(Name* name, Map* map, Code* code);
Code* Get(Name* name, Map* map);
Object* Set(Name* name, Map* map, Object* handler);
Object* Get(Name* name, Map* map);
// Clear the lookup table (@ mark compact collection).
void Clear();
// Collect all maps that match the name.
......
......@@ -5,7 +5,7 @@
#include "src/type-feedback-vector.h"
#include "src/code-stubs.h"
#include "src/ic/ic.h"
#include "src/ic/ic-inl.h"
#include "src/ic/ic-state.h"
#include "src/objects.h"
#include "src/type-feedback-vector-inl.h"
......@@ -851,17 +851,10 @@ int GetStepSize(FixedArray* array, Isolate* isolate) {
DCHECK(array->length() >= 2);
Object* second = array->get(1);
if (second->IsWeakCell() || second->IsUndefined(isolate)) return 3;
DCHECK(second->IsCode() || second->IsSmi());
DCHECK(IC::IsHandler(second));
return 2;
}
#ifdef DEBUG // Only used by DCHECKs below.
bool IsHandler(Object* object) {
return object->IsSmi() ||
(object->IsCode() && Code::cast(object)->is_handler());
}
#endif
} // namespace
int FeedbackNexus::ExtractMaps(MapHandleList* maps) const {
......@@ -914,7 +907,7 @@ MaybeHandle<Object> FeedbackNexus::FindHandlerForMap(Handle<Map> map) const {
Map* array_map = Map::cast(cell->value());
if (array_map == *map) {
Object* code = array->get(i + increment - 1);
DCHECK(IsHandler(code));
DCHECK(IC::IsHandler(code));
return handle(code, isolate);
}
}
......@@ -925,7 +918,7 @@ MaybeHandle<Object> FeedbackNexus::FindHandlerForMap(Handle<Map> map) const {
Map* cell_map = Map::cast(cell->value());
if (cell_map == *map) {
Object* code = GetFeedbackExtra();
DCHECK(IsHandler(code));
DCHECK(IC::IsHandler(code));
return handle(code, isolate);
}
}
......@@ -952,7 +945,7 @@ bool FeedbackNexus::FindHandlers(List<Handle<Object>>* code_list,
// Be sure to skip handlers whose maps have been cleared.
if (!cell->cleared()) {
Object* code = array->get(i + increment - 1);
DCHECK(IsHandler(code));
DCHECK(IC::IsHandler(code));
code_list->Add(handle(code, isolate));
count++;
}
......@@ -961,7 +954,7 @@ bool FeedbackNexus::FindHandlers(List<Handle<Object>>* code_list,
WeakCell* cell = WeakCell::cast(feedback);
if (!cell->cleared()) {
Object* code = GetFeedbackExtra();
DCHECK(IsHandler(code));
DCHECK(IC::IsHandler(code));
code_list->Add(handle(code, isolate));
count++;
}
......
......@@ -1346,14 +1346,14 @@ TEST(TryProbeStubCache) {
int index = rand_gen.NextInt();
Handle<Name> name = names[index % names.size()];
Handle<JSObject> receiver = receivers[index % receivers.size()];
Code* handler = stub_cache.Get(*name, receiver->map());
Object* handler = stub_cache.Get(*name, receiver->map());
if (handler == nullptr) {
queried_non_existing = true;
} else {
queried_existing = true;
}
Handle<Code> expected_handler(handler, isolate);
Handle<Object> expected_handler(handler, isolate);
ft.CheckTrue(receiver, name, expected_handler);
}
......@@ -1362,14 +1362,14 @@ TEST(TryProbeStubCache) {
int index2 = rand_gen.NextInt();
Handle<Name> name = names[index1 % names.size()];
Handle<JSObject> receiver = receivers[index2 % receivers.size()];
Code* handler = stub_cache.Get(*name, receiver->map());
Object* handler = stub_cache.Get(*name, receiver->map());
if (handler == nullptr) {
queried_non_existing = true;
} else {
queried_existing = true;
}
Handle<Code> expected_handler(handler, isolate);
Handle<Object> expected_handler(handler, isolate);
ft.CheckTrue(receiver, name, expected_handler);
}
// Ensure we performed both kind of queries.
......
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