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