1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/ic/stub-cache.h" 6 7 #include "src/ast/ast.h" 8 #include "src/base/bits.h" 9 #include "src/counters.h" 10 #include "src/heap/heap.h" 11 #include "src/ic/ic-inl.h" 12 #include "src/type-info.h" 13 14 namespace v8 { 15 namespace internal { 16 17 StubCache::StubCache(Isolate* isolate, Code::Kind ic_kind) 18 : isolate_(isolate), ic_kind_(ic_kind) { 19 // Ensure the nullptr (aka Smi::kZero) which StubCache::Get() returns 20 // when the entry is not found is not considered as a handler. 21 DCHECK(!IC::IsHandler(nullptr)); 22 } 23 24 void StubCache::Initialize() { 25 DCHECK(base::bits::IsPowerOfTwo32(kPrimaryTableSize)); 26 DCHECK(base::bits::IsPowerOfTwo32(kSecondaryTableSize)); 27 Clear(); 28 } 29 30 #ifdef DEBUG 31 namespace { 32 33 bool CommonStubCacheChecks(StubCache* stub_cache, Name* name, Map* map, 34 Object* handler) { 35 // Validate that the name and handler do not move on scavenge, and that we 36 // can use identity checks instead of structural equality checks. 37 DCHECK(!name->GetHeap()->InNewSpace(name)); 38 DCHECK(!name->GetHeap()->InNewSpace(handler)); 39 DCHECK(name->IsUniqueName()); 40 DCHECK(name->HasHashCode()); 41 if (handler) { 42 DCHECK(IC::IsHandler(handler)); 43 if (handler->IsCode()) { 44 Code* code = Code::cast(handler); 45 Code::Flags expected_flags = Code::RemoveHolderFromFlags( 46 Code::ComputeHandlerFlags(stub_cache->ic_kind())); 47 Code::Flags flags = Code::RemoveHolderFromFlags(code->flags()); 48 DCHECK_EQ(expected_flags, flags); 49 DCHECK_EQ(Code::HANDLER, Code::ExtractKindFromFlags(code->flags())); 50 } 51 } 52 return true; 53 } 54 55 } // namespace 56 #endif 57 58 Object* StubCache::Set(Name* name, Map* map, Object* handler) { 59 DCHECK(CommonStubCacheChecks(this, name, map, handler)); 60 61 // Compute the primary entry. 62 int primary_offset = PrimaryOffset(name, map); 63 Entry* primary = entry(primary_, primary_offset); 64 Object* old_handler = primary->value; 65 66 // If the primary entry has useful data in it, we retire it to the 67 // secondary cache before overwriting it. 68 if (old_handler != isolate_->builtins()->builtin(Builtins::kIllegal)) { 69 Map* old_map = primary->map; 70 int seed = PrimaryOffset(primary->key, old_map); 71 int secondary_offset = SecondaryOffset(primary->key, seed); 72 Entry* secondary = entry(secondary_, secondary_offset); 73 *secondary = *primary; 74 } 75 76 // Update primary cache. 77 primary->key = name; 78 primary->value = handler; 79 primary->map = map; 80 isolate()->counters()->megamorphic_stub_cache_updates()->Increment(); 81 return handler; 82 } 83 84 Object* StubCache::Get(Name* name, Map* map) { 85 DCHECK(CommonStubCacheChecks(this, name, map, nullptr)); 86 int primary_offset = PrimaryOffset(name, map); 87 Entry* primary = entry(primary_, primary_offset); 88 if (primary->key == name && primary->map == map) { 89 return primary->value; 90 } 91 int secondary_offset = SecondaryOffset(name, primary_offset); 92 Entry* secondary = entry(secondary_, secondary_offset); 93 if (secondary->key == name && secondary->map == map) { 94 return secondary->value; 95 } 96 return nullptr; 97 } 98 99 100 void StubCache::Clear() { 101 Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); 102 for (int i = 0; i < kPrimaryTableSize; i++) { 103 primary_[i].key = isolate()->heap()->empty_string(); 104 primary_[i].map = nullptr; 105 primary_[i].value = empty; 106 } 107 for (int j = 0; j < kSecondaryTableSize; j++) { 108 secondary_[j].key = isolate()->heap()->empty_string(); 109 secondary_[j].map = nullptr; 110 secondary_[j].value = empty; 111 } 112 } 113 114 115 void StubCache::CollectMatchingMaps(SmallMapList* types, Handle<Name> name, 116 Handle<Context> native_context, 117 Zone* zone) { 118 for (int i = 0; i < kPrimaryTableSize; i++) { 119 if (primary_[i].key == *name) { 120 Map* map = primary_[i].map; 121 // Map can be nullptr, if the stub is constant function call 122 // with a primitive receiver. 123 if (map == nullptr) continue; 124 125 int offset = PrimaryOffset(*name, map); 126 if (entry(primary_, offset) == &primary_[i] && 127 TypeFeedbackOracle::IsRelevantFeedback(map, *native_context)) { 128 types->AddMapIfMissing(Handle<Map>(map), zone); 129 } 130 } 131 } 132 133 for (int i = 0; i < kSecondaryTableSize; i++) { 134 if (secondary_[i].key == *name) { 135 Map* map = secondary_[i].map; 136 // Map can be nullptr, if the stub is constant function call 137 // with a primitive receiver. 138 if (map == nullptr) continue; 139 140 // Lookup in primary table and skip duplicates. 141 int primary_offset = PrimaryOffset(*name, map); 142 143 // Lookup in secondary table and add matches. 144 int offset = SecondaryOffset(*name, primary_offset); 145 if (entry(secondary_, offset) == &secondary_[i] && 146 TypeFeedbackOracle::IsRelevantFeedback(map, *native_context)) { 147 types->AddMapIfMissing(Handle<Map>(map), zone); 148 } 149 } 150 } 151 } 152 } // namespace internal 153 } // namespace v8 154