Index: src/ic/stub-cache.cc |
diff --git a/src/ic/stub-cache.cc b/src/ic/stub-cache.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e63fbfe9e50c1991056e3e69ce70dcc01742b7a2 |
--- /dev/null |
+++ b/src/ic/stub-cache.cc |
@@ -0,0 +1,146 @@ |
+// Copyright 2012 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/v8.h" |
+ |
+#include "src/ic/stub-cache.h" |
+#include "src/type-info.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+ |
+StubCache::StubCache(Isolate* isolate) : isolate_(isolate) {} |
+ |
+ |
+void StubCache::Initialize() { |
+ DCHECK(IsPowerOf2(kPrimaryTableSize)); |
+ DCHECK(IsPowerOf2(kSecondaryTableSize)); |
+ Clear(); |
+} |
+ |
+ |
+static Code::Flags CommonStubCacheChecks(Name* name, Map* map, |
+ Code::Flags flags) { |
+ flags = Code::RemoveTypeAndHolderFromFlags(flags); |
+ |
+ // 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()); |
+ |
+ // The state bits are not important to the hash function because the stub |
+ // cache only contains handlers. Make sure that the bits are the least |
+ // significant so they will be the ones masked out. |
+ DCHECK_EQ(Code::HANDLER, Code::ExtractKindFromFlags(flags)); |
+ STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1); |
+ |
+ // Make sure that the code type and cache holder are not included in the hash. |
+ DCHECK(Code::ExtractTypeFromFlags(flags) == 0); |
+ DCHECK(Code::ExtractCacheHolderFromFlags(flags) == 0); |
+ |
+ return flags; |
+} |
+ |
+ |
+Code* StubCache::Set(Name* name, Map* map, Code* code) { |
+ Code::Flags flags = CommonStubCacheChecks(name, map, code->flags()); |
+ |
+ // Compute the primary entry. |
+ int primary_offset = PrimaryOffset(name, flags, map); |
+ Entry* primary = entry(primary_, primary_offset); |
+ Code* old_code = 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)) { |
+ Map* old_map = primary->map; |
+ Code::Flags old_flags = |
+ Code::RemoveTypeAndHolderFromFlags(old_code->flags()); |
+ int seed = PrimaryOffset(primary->key, old_flags, old_map); |
+ int secondary_offset = SecondaryOffset(primary->key, old_flags, seed); |
+ Entry* secondary = entry(secondary_, secondary_offset); |
+ *secondary = *primary; |
+ } |
+ |
+ // Update primary cache. |
+ primary->key = name; |
+ primary->value = code; |
+ primary->map = map; |
+ isolate()->counters()->megamorphic_stub_cache_updates()->Increment(); |
+ return code; |
+} |
+ |
+ |
+Code* StubCache::Get(Name* name, Map* map, Code::Flags flags) { |
+ flags = CommonStubCacheChecks(name, map, flags); |
+ int primary_offset = PrimaryOffset(name, flags, map); |
+ Entry* primary = entry(primary_, primary_offset); |
+ if (primary->key == name && primary->map == map) { |
+ return primary->value; |
+ } |
+ int secondary_offset = SecondaryOffset(name, flags, primary_offset); |
+ Entry* secondary = entry(secondary_, secondary_offset); |
+ if (secondary->key == name && secondary->map == map) { |
+ return secondary->value; |
+ } |
+ return NULL; |
+} |
+ |
+ |
+void StubCache::Clear() { |
+ Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); |
+ for (int i = 0; i < kPrimaryTableSize; i++) { |
+ primary_[i].key = isolate()->heap()->empty_string(); |
+ primary_[i].map = NULL; |
+ primary_[i].value = empty; |
+ } |
+ for (int j = 0; j < kSecondaryTableSize; j++) { |
+ secondary_[j].key = isolate()->heap()->empty_string(); |
+ secondary_[j].map = NULL; |
+ secondary_[j].value = empty; |
+ } |
+} |
+ |
+ |
+void StubCache::CollectMatchingMaps(SmallMapList* types, Handle<Name> name, |
+ Code::Flags flags, |
+ Handle<Context> native_context, |
+ Zone* zone) { |
+ for (int i = 0; i < kPrimaryTableSize; i++) { |
+ if (primary_[i].key == *name) { |
+ Map* map = primary_[i].map; |
+ // Map can be NULL, if the stub is constant function call |
+ // with a primitive receiver. |
+ if (map == NULL) continue; |
+ |
+ int offset = PrimaryOffset(*name, flags, map); |
+ if (entry(primary_, offset) == &primary_[i] && |
+ !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { |
+ types->AddMapIfMissing(Handle<Map>(map), zone); |
+ } |
+ } |
+ } |
+ |
+ for (int i = 0; i < kSecondaryTableSize; i++) { |
+ if (secondary_[i].key == *name) { |
+ Map* map = secondary_[i].map; |
+ // Map can be NULL, if the stub is constant function call |
+ // with a primitive receiver. |
+ if (map == NULL) continue; |
+ |
+ // Lookup in primary table and skip duplicates. |
+ int primary_offset = PrimaryOffset(*name, flags, map); |
+ |
+ // Lookup in secondary table and add matches. |
+ int offset = SecondaryOffset(*name, flags, primary_offset); |
+ if (entry(secondary_, offset) == &secondary_[i] && |
+ !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { |
+ types->AddMapIfMissing(Handle<Map>(map), zone); |
+ } |
+ } |
+ } |
+} |
+} |
+} // namespace v8::internal |