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.h"
11 : #include "src/ic/ic-inl.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 125771 : 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 125771 : }
21 :
22 125766 : void StubCache::Initialize() {
23 : DCHECK(base::bits::IsPowerOfTwo(kPrimaryTableSize));
24 : DCHECK(base::bits::IsPowerOfTwo(kSecondaryTableSize));
25 125766 : Clear();
26 125766 : }
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 1203842 : uint32_t map_low32bits = static_cast<uint32_t>(map.ptr());
40 : // Base the offset on a simple combination of name and map.
41 1203842 : uint32_t key = map_low32bits + field;
42 1203842 : return key & ((kPrimaryTableSize - 1) << kCacheIndexShift);
43 : }
44 :
45 : // Hash algorithm for the secondary table. This algorithm is replicated in
46 : // assembler for every architecture. Returns an index into the table that
47 : // is scaled by 1 << kCacheIndexShift.
48 0 : int StubCache::SecondaryOffset(Name name, int seed) {
49 : // Use the seed from the primary cache in the secondary cache.
50 483533 : uint32_t name_low32bits = static_cast<uint32_t>(name.ptr());
51 483533 : uint32_t key = (seed - name_low32bits) + kSecondaryMagic;
52 483533 : return key & ((kSecondaryTableSize - 1) << kCacheIndexShift);
53 : }
54 :
55 1100 : int StubCache::PrimaryOffsetForTesting(Name name, Map map) {
56 1100 : return PrimaryOffset(name, map);
57 : }
58 :
59 550 : int StubCache::SecondaryOffsetForTesting(Name name, int seed) {
60 550 : return SecondaryOffset(name, seed);
61 : }
62 :
63 : #ifdef DEBUG
64 : namespace {
65 :
66 : bool CommonStubCacheChecks(StubCache* stub_cache, Name name, Map map,
67 : MaybeObject handler) {
68 : // Validate that the name and handler do not move on scavenge, and that we
69 : // can use identity checks instead of structural equality checks.
70 : DCHECK(!Heap::InNewSpace(name));
71 : DCHECK(!Heap::InNewSpace(handler));
72 : DCHECK(name->IsUniqueName());
73 : DCHECK(name->HasHashCode());
74 : if (handler->ptr() != kNullAddress) DCHECK(IC::IsHandler(handler));
75 : return true;
76 : }
77 :
78 : } // namespace
79 : #endif
80 :
81 1437120 : void StubCache::Set(Name name, Map map, MaybeObject handler) {
82 : DCHECK(CommonStubCacheChecks(this, name, map, handler));
83 :
84 : // Compute the primary entry.
85 : int primary_offset = PrimaryOffset(name, map);
86 718559 : Entry* primary = entry(primary_, primary_offset);
87 718559 : MaybeObject old_handler(primary->value);
88 :
89 : // If the primary entry has useful data in it, we retire it to the
90 : // secondary cache before overwriting it.
91 718561 : if (old_handler != MaybeObject::FromObject(
92 1177142 : isolate_->builtins()->builtin(Builtins::kIllegal)) &&
93 458583 : primary->map != kNullAddress) {
94 458583 : Map old_map = Map::cast(Object(primary->map));
95 458583 : int seed = PrimaryOffset(Name::cast(Object(primary->key)), old_map);
96 : int secondary_offset =
97 458583 : SecondaryOffset(Name::cast(Object(primary->key)), seed);
98 458583 : Entry* secondary = entry(secondary_, secondary_offset);
99 458583 : *secondary = *primary;
100 : }
101 :
102 : // Update primary cache.
103 718561 : primary->key = name.ptr();
104 718561 : primary->value = handler.ptr();
105 718561 : primary->map = map.ptr();
106 718561 : isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
107 718560 : }
108 :
109 25600 : MaybeObject StubCache::Get(Name name, Map map) {
110 : DCHECK(CommonStubCacheChecks(this, name, map, MaybeObject()));
111 : int primary_offset = PrimaryOffset(name, map);
112 25600 : Entry* primary = entry(primary_, primary_offset);
113 25600 : if (primary->key == name.ptr() && primary->map == map.ptr()) {
114 1200 : return MaybeObject(primary->value);
115 : }
116 : int secondary_offset = SecondaryOffset(name, primary_offset);
117 24400 : Entry* secondary = entry(secondary_, secondary_offset);
118 24400 : if (secondary->key == name.ptr() && secondary->map == map.ptr()) {
119 395 : return MaybeObject(secondary->value);
120 : }
121 24005 : return MaybeObject();
122 : }
123 :
124 585510 : void StubCache::Clear() {
125 : MaybeObject empty = MaybeObject::FromObject(
126 292755 : isolate_->builtins()->builtin(Builtins::kIllegal));
127 : Name empty_string = ReadOnlyRoots(isolate()).empty_string();
128 599848960 : for (int i = 0; i < kPrimaryTableSize; i++) {
129 599556205 : primary_[i].key = empty_string.ptr();
130 599556205 : primary_[i].map = kNullAddress;
131 599556205 : primary_[i].value = empty.ptr();
132 : }
133 149764096 : for (int j = 0; j < kSecondaryTableSize; j++) {
134 149764096 : secondary_[j].key = empty_string.ptr();
135 149764096 : secondary_[j].map = kNullAddress;
136 149764096 : secondary_[j].value = empty.ptr();
137 : }
138 292755 : }
139 :
140 : } // namespace internal
141 183867 : } // namespace v8
|