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 124887 : 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 124887 : }
21 :
22 124883 : void StubCache::Initialize() {
23 : DCHECK(base::bits::IsPowerOfTwo(kPrimaryTableSize));
24 : DCHECK(base::bits::IsPowerOfTwo(kSecondaryTableSize));
25 124883 : Clear();
26 124884 : }
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 1120110 : static_cast<uint32_t>(map.ptr() ^ (map.ptr() >> kMapKeyShift));
41 : // Base the offset on a simple combination of name and map.
42 1120110 : uint32_t key = map_low32bits + field;
43 1120110 : 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 408254 : uint32_t name_low32bits = static_cast<uint32_t>(name.ptr());
52 408254 : uint32_t key = (seed - name_low32bits) + kSecondaryMagic;
53 408254 : 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 710436 : 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 710436 : Entry* primary = entry(primary_, primary_offset);
88 710436 : 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 710437 : if (old_handler != MaybeObject::FromObject(
93 1809186 : isolate_->builtins()->builtin(Builtins::kIllegal)) &&
94 388314 : primary->map != kNullAddress) {
95 388314 : Map old_map = Map::cast(Object(primary->map));
96 388314 : int seed = PrimaryOffset(Name::cast(Object(primary->key)), old_map);
97 : int secondary_offset =
98 : SecondaryOffset(Name::cast(Object(primary->key)), seed);
99 388314 : Entry* secondary = entry(secondary_, secondary_offset);
100 388314 : *secondary = *primary;
101 : }
102 :
103 : // Update primary cache.
104 710437 : primary->key = name.ptr();
105 710437 : primary->value = handler.ptr();
106 710437 : primary->map = map.ptr();
107 710437 : isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
108 710437 : }
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 980 : return MaybeObject(primary->value);
116 : }
117 : int secondary_offset = SecondaryOffset(name, primary_offset);
118 19500 : Entry* secondary = entry(secondary_, secondary_offset);
119 19500 : if (secondary->key == name.ptr() && secondary->map == map.ptr()) {
120 276 : return MaybeObject(secondary->value);
121 : }
122 19224 : return MaybeObject();
123 : }
124 :
125 262579 : void StubCache::Clear() {
126 : MaybeObject empty = MaybeObject::FromObject(
127 525158 : isolate_->builtins()->builtin(Builtins::kIllegal));
128 : Name empty_string = ReadOnlyRoots(isolate()).empty_string();
129 1074340276 : for (int i = 0; i < kPrimaryTableSize; i++) {
130 537038848 : primary_[i].key = empty_string.ptr();
131 537038848 : primary_[i].map = kNullAddress;
132 537038848 : primary_[i].value = empty.ptr();
133 : }
134 268917172 : for (int j = 0; j < kSecondaryTableSize; j++) {
135 134327296 : secondary_[j].key = empty_string.ptr();
136 134327296 : secondary_[j].map = kNullAddress;
137 134327296 : secondary_[j].value = empty.ptr();
138 : }
139 262580 : }
140 :
141 : } // namespace internal
142 122036 : } // namespace v8
|