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