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