| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index de0d756966db9720d9a284d1f5d3a565add14ea1..2d47b8f3651925247285cfe2f455be09681c2d9e 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -9447,29 +9447,186 @@ Handle<Map> Map::CopyReplaceDescriptor(Handle<Map> map,
|
| simple_flag);
|
| }
|
|
|
| +// Helper class to manage a Map's code cache. The layout depends on the number
|
| +// of entries; this is worthwhile because most code caches are very small,
|
| +// but some are huge (thousands of entries).
|
| +// For zero entries, the EmptyFixedArray is used.
|
| +// For one entry, we use a 2-element FixedArray containing [name, code].
|
| +// For 2..100 entries, we use a FixedArray with linear lookups, the layout is:
|
| +// [0] - number of slots that are currently in use
|
| +// [1] - first name
|
| +// [2] - first code
|
| +// [3] - second name
|
| +// [4] - second code
|
| +// etc.
|
| +// For more than 128 entries, we use a CodeCacheHashTable.
|
| +class CodeCache : public AllStatic {
|
| + public:
|
| + // Returns the new cache, to be stored on the map.
|
| + static Handle<FixedArray> Put(Isolate* isolate, Handle<FixedArray> cache,
|
| + Handle<Name> name, Handle<Code> code) {
|
| + int length = cache->length();
|
| + if (length == 0) return PutFirstElement(isolate, name, code);
|
| + if (length == kEntrySize) {
|
| + return PutSecondElement(isolate, cache, name, code);
|
| + }
|
| + if (length <= kLinearMaxSize) {
|
| + Handle<FixedArray> result = PutLinearElement(isolate, cache, name, code);
|
| + if (!result.is_null()) return result;
|
| + // Fall through if linear storage is getting too large.
|
| + }
|
| + return PutHashTableElement(isolate, cache, name, code);
|
| + }
|
| +
|
| + static Code* Lookup(FixedArray* cache, Name* name, Code::Flags flags) {
|
| + int length = cache->length();
|
| + if (length == 0) return nullptr;
|
| + if (length == kEntrySize) return OneElementLookup(cache, name, flags);
|
| + if (!cache->IsCodeCacheHashTable()) {
|
| + return LinearLookup(cache, name, flags);
|
| + } else {
|
| + return CodeCacheHashTable::cast(cache)->Lookup(name, flags);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + static const int kNameIndex = 0;
|
| + static const int kCodeIndex = 1;
|
| + static const int kEntrySize = 2;
|
| +
|
| + static const int kLinearUsageIndex = 0;
|
| + static const int kLinearReservedSlots = 1;
|
| + static const int kLinearInitialCapacity = 2;
|
| + static const int kLinearMaxSize = 257; // == LinearSizeFor(128);
|
| +
|
| + static const int kHashTableInitialCapacity = 200; // Number of entries.
|
| +
|
| + static int LinearSizeFor(int entries) {
|
| + return kLinearReservedSlots + kEntrySize * entries;
|
| + }
|
| +
|
| + static int LinearNewSize(int old_size) {
|
| + int old_entries = (old_size - kLinearReservedSlots) / kEntrySize;
|
| + return LinearSizeFor(old_entries * 2);
|
| + }
|
| +
|
| + static Code* OneElementLookup(FixedArray* cache, Name* name,
|
| + Code::Flags flags) {
|
| + DCHECK_EQ(cache->length(), kEntrySize);
|
| + if (cache->get(kNameIndex) != name) return nullptr;
|
| + Code* maybe_code = Code::cast(cache->get(kCodeIndex));
|
| + if (maybe_code->flags() != flags) return nullptr;
|
| + return maybe_code;
|
| + }
|
| +
|
| + static Code* LinearLookup(FixedArray* cache, Name* name, Code::Flags flags) {
|
| + DCHECK_GE(cache->length(), kEntrySize);
|
| + DCHECK(!cache->IsCodeCacheHashTable());
|
| + int usage = GetLinearUsage(cache);
|
| + for (int i = kLinearReservedSlots; i < usage; i += kEntrySize) {
|
| + if (cache->get(i + kNameIndex) != name) continue;
|
| + Code* code = Code::cast(cache->get(i + kCodeIndex));
|
| + if (code->flags() == flags) return code;
|
| + }
|
| + return nullptr;
|
| + }
|
| +
|
| + static Handle<FixedArray> PutFirstElement(Isolate* isolate, Handle<Name> name,
|
| + Handle<Code> code) {
|
| + Handle<FixedArray> cache = isolate->factory()->NewFixedArray(kEntrySize);
|
| + cache->set(kNameIndex, *name);
|
| + cache->set(kCodeIndex, *code);
|
| + return cache;
|
| + }
|
| +
|
| + static Handle<FixedArray> PutSecondElement(Isolate* isolate,
|
| + Handle<FixedArray> cache,
|
| + Handle<Name> name,
|
| + Handle<Code> code) {
|
| + DCHECK_EQ(cache->length(), kEntrySize);
|
| + Handle<FixedArray> new_cache = isolate->factory()->NewFixedArray(
|
| + LinearSizeFor(kLinearInitialCapacity));
|
| + new_cache->set(kLinearReservedSlots + kNameIndex, cache->get(kNameIndex));
|
| + new_cache->set(kLinearReservedSlots + kCodeIndex, cache->get(kCodeIndex));
|
| + new_cache->set(LinearSizeFor(1) + kNameIndex, *name);
|
| + new_cache->set(LinearSizeFor(1) + kCodeIndex, *code);
|
| + new_cache->set(kLinearUsageIndex, Smi::FromInt(LinearSizeFor(2)));
|
| + return new_cache;
|
| + }
|
| +
|
| + static Handle<FixedArray> PutLinearElement(Isolate* isolate,
|
| + Handle<FixedArray> cache,
|
| + Handle<Name> name,
|
| + Handle<Code> code) {
|
| + int length = cache->length();
|
| + int usage = GetLinearUsage(*cache);
|
| + DCHECK_LE(usage, length);
|
| + // Check if we need to grow.
|
| + if (usage == length) {
|
| + int new_length = LinearNewSize(length);
|
| + if (new_length > kLinearMaxSize) return Handle<FixedArray>::null();
|
| + Handle<FixedArray> new_cache =
|
| + isolate->factory()->NewFixedArray(new_length);
|
| + for (int i = kLinearReservedSlots; i < length; i++) {
|
| + new_cache->set(i, cache->get(i));
|
| + }
|
| + cache = new_cache;
|
| + }
|
| + // Store new entry.
|
| + DCHECK_GE(cache->length(), usage + kEntrySize);
|
| + cache->set(usage + kNameIndex, *name);
|
| + cache->set(usage + kCodeIndex, *code);
|
| + cache->set(kLinearUsageIndex, Smi::FromInt(usage + kEntrySize));
|
| + return cache;
|
| + }
|
| +
|
| + static Handle<FixedArray> PutHashTableElement(Isolate* isolate,
|
| + Handle<FixedArray> cache,
|
| + Handle<Name> name,
|
| + Handle<Code> code) {
|
| + // Check if we need to transition from linear to hash table storage.
|
| + if (!cache->IsCodeCacheHashTable()) {
|
| + // Check that the initial hash table capacity is large enough.
|
| + DCHECK_EQ(kLinearMaxSize, LinearSizeFor(128));
|
| + STATIC_ASSERT(kHashTableInitialCapacity > 128);
|
| +
|
| + int length = cache->length();
|
| + // Only migrate from linear storage when it's full.
|
| + DCHECK_EQ(length, GetLinearUsage(*cache));
|
| + DCHECK_EQ(length, kLinearMaxSize);
|
| + Handle<CodeCacheHashTable> table =
|
| + CodeCacheHashTable::New(isolate, kHashTableInitialCapacity);
|
| + HandleScope scope(isolate);
|
| + for (int i = kLinearReservedSlots; i < length; i += kEntrySize) {
|
| + Handle<Name> old_name(Name::cast(cache->get(i + kNameIndex)), isolate);
|
| + Handle<Code> old_code(Code::cast(cache->get(i + kCodeIndex)), isolate);
|
| + CodeCacheHashTable::Put(table, old_name, old_code);
|
| + }
|
| + cache = table;
|
| + }
|
| + // Store new entry.
|
| + DCHECK(cache->IsCodeCacheHashTable());
|
| + return CodeCacheHashTable::Put(Handle<CodeCacheHashTable>::cast(cache),
|
| + name, code);
|
| + }
|
| +
|
| + static inline int GetLinearUsage(FixedArray* linear_cache) {
|
| + DCHECK_GT(linear_cache->length(), kEntrySize);
|
| + return Smi::cast(linear_cache->get(kLinearUsageIndex))->value();
|
| + }
|
| +};
|
|
|
| void Map::UpdateCodeCache(Handle<Map> map,
|
| Handle<Name> name,
|
| Handle<Code> code) {
|
| Isolate* isolate = map->GetIsolate();
|
| - HandleScope scope(isolate);
|
| - // Allocate the code cache if not present.
|
| - if (!map->has_code_cache()) {
|
| - Handle<Object> result =
|
| - CodeCacheHashTable::New(isolate, CodeCacheHashTable::kInitialSize);
|
| - map->set_code_cache(*result);
|
| - }
|
| -
|
| - // Update the code cache.
|
| - Handle<CodeCacheHashTable> cache(CodeCacheHashTable::cast(map->code_cache()),
|
| - isolate);
|
| - Handle<Object> new_cache = CodeCacheHashTable::Put(cache, name, code);
|
| + Handle<FixedArray> cache(map->code_cache(), isolate);
|
| + Handle<FixedArray> new_cache = CodeCache::Put(isolate, cache, name, code);
|
| map->set_code_cache(*new_cache);
|
| }
|
|
|
| Code* Map::LookupInCodeCache(Name* name, Code::Flags flags) {
|
| - if (!has_code_cache()) return nullptr;
|
| - return CodeCacheHashTable::cast(code_cache())->Lookup(name, flags);
|
| + return CodeCache::Lookup(code_cache(), name, flags);
|
| }
|
|
|
|
|
|
|