Line data Source code
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-inl.h" // For InYoungGeneration().
11 : #include "src/ic/ic-inl.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 124848 : StubCache::StubCache(Isolate* isolate) : isolate_(isolate) {
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(MaybeObject()));
20 124848 : }
21 :
22 124844 : void StubCache::Initialize() {
23 : DCHECK(base::bits::IsPowerOfTwo(kPrimaryTableSize));
24 : DCHECK(base::bits::IsPowerOfTwo(kSecondaryTableSize));
25 124844 : Clear();
26 124844 : }
27 :
28 : // Hash algorithm for the primary table. This algorithm is replicated in
29 : // assembler for every architecture. Returns an index into the table that
30 : // is scaled by 1 << kCacheIndexShift.
31 0 : int StubCache::PrimaryOffset(Name name, Map map) {
32 : STATIC_ASSERT(kCacheIndexShift == Name::kHashShift);
33 : // Compute the hash of the name (use entire hash field).
34 : DCHECK(name->HasHashCode());
35 : uint32_t field = name->hash_field();
36 : // Using only the low bits in 64-bit mode is unlikely to increase the
37 : // risk of collision even if the heap is spread over an area larger than
38 : // 4Gb (and not at all if it isn't).
39 : uint32_t map_low32bits =
40 1124859 : static_cast<uint32_t>(map.ptr() ^ (map.ptr() >> kMapKeyShift));
41 : // Base the offset on a simple combination of name and map.
42 1124859 : uint32_t key = map_low32bits + field;
43 1124859 : return key & ((kPrimaryTableSize - 1) << kCacheIndexShift);
44 : }
45 :
46 : // Hash algorithm for the secondary table. This algorithm is replicated in
47 : // assembler for every architecture. Returns an index into the table that
48 : // is scaled by 1 << kCacheIndexShift.
49 0 : int StubCache::SecondaryOffset(Name name, int seed) {
50 : // Use the seed from the primary cache in the secondary cache.
51 412849 : uint32_t name_low32bits = static_cast<uint32_t>(name.ptr());
52 412849 : uint32_t key = (seed - name_low32bits) + kSecondaryMagic;
53 412849 : return key & ((kSecondaryTableSize - 1) << kCacheIndexShift);
54 : }
55 :
56 880 : int StubCache::PrimaryOffsetForTesting(Name name, Map map) {
57 880 : return PrimaryOffset(name, map);
58 : }
59 :
60 440 : int StubCache::SecondaryOffsetForTesting(Name name, int seed) {
61 440 : return SecondaryOffset(name, seed);
62 : }
63 :
64 : #ifdef DEBUG
65 : namespace {
66 :
67 : bool CommonStubCacheChecks(StubCache* stub_cache, Name name, Map map,
68 : MaybeObject handler) {
69 : // Validate that the name and handler do not move on scavenge, and that we
70 : // can use identity checks instead of structural equality checks.
71 : DCHECK(!Heap::InYoungGeneration(name));
72 : DCHECK(!Heap::InYoungGeneration(handler));
73 : DCHECK(name->IsUniqueName());
74 : DCHECK(name->HasHashCode());
75 : if (handler->ptr() != kNullAddress) DCHECK(IC::IsHandler(handler));
76 : return true;
77 : }
78 :
79 : } // namespace
80 : #endif
81 :
82 710642 : void StubCache::Set(Name name, Map map, MaybeObject handler) {
83 : DCHECK(CommonStubCacheChecks(this, name, map, handler));
84 :
85 : // Compute the primary entry.
86 : int primary_offset = PrimaryOffset(name, map);
87 710642 : Entry* primary = entry(primary_, primary_offset);
88 710642 : MaybeObject old_handler(primary->value);
89 :
90 : // If the primary entry has useful data in it, we retire it to the
91 : // secondary cache before overwriting it.
92 710642 : if (old_handler != MaybeObject::FromObject(
93 1814141 : isolate_->builtins()->builtin(Builtins::kIllegal)) &&
94 392857 : primary->map != kNullAddress) {
95 392857 : Map old_map = Map::cast(Object(primary->map));
96 392857 : int seed = PrimaryOffset(Name::cast(Object(primary->key)), old_map);
97 : int secondary_offset =
98 : SecondaryOffset(Name::cast(Object(primary->key)), seed);
99 392857 : Entry* secondary = entry(secondary_, secondary_offset);
100 392857 : *secondary = *primary;
101 : }
102 :
103 : // Update primary cache.
104 710642 : primary->key = name.ptr();
105 710642 : primary->value = handler.ptr();
106 710642 : primary->map = map.ptr();
107 710642 : isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
108 710642 : }
109 :
110 20480 : MaybeObject StubCache::Get(Name name, Map map) {
111 : DCHECK(CommonStubCacheChecks(this, name, map, MaybeObject()));
112 : int primary_offset = PrimaryOffset(name, map);
113 20480 : Entry* primary = entry(primary_, primary_offset);
114 20480 : if (primary->key == name.ptr() && primary->map == map.ptr()) {
115 928 : return MaybeObject(primary->value);
116 : }
117 : int secondary_offset = SecondaryOffset(name, primary_offset);
118 19552 : Entry* secondary = entry(secondary_, secondary_offset);
119 19552 : if (secondary->key == name.ptr() && secondary->map == map.ptr()) {
120 316 : return MaybeObject(secondary->value);
121 : }
122 19236 : return MaybeObject();
123 : }
124 :
125 262576 : void StubCache::Clear() {
126 : MaybeObject empty = MaybeObject::FromObject(
127 525152 : isolate_->builtins()->builtin(Builtins::kIllegal));
128 : Name empty_string = ReadOnlyRoots(isolate()).empty_string();
129 1074291120 : for (int i = 0; i < kPrimaryTableSize; i++) {
130 537014272 : primary_[i].key = empty_string.ptr();
131 537014272 : primary_[i].map = kNullAddress;
132 537014272 : primary_[i].value = empty.ptr();
133 : }
134 268920240 : for (int j = 0; j < kSecondaryTableSize; j++) {
135 134328832 : secondary_[j].key = empty_string.ptr();
136 134328832 : secondary_[j].map = kNullAddress;
137 134328832 : secondary_[j].value = empty.ptr();
138 : }
139 262576 : }
140 :
141 : } // namespace internal
142 121996 : } // namespace v8
|