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 123070 : 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 123070 : }
21 :
22 123068 : void StubCache::Initialize() {
23 : DCHECK(base::bits::IsPowerOfTwo(kPrimaryTableSize));
24 : DCHECK(base::bits::IsPowerOfTwo(kSecondaryTableSize));
25 123068 : Clear();
26 123068 : }
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 1134025 : static_cast<uint32_t>(map.ptr() ^ (map.ptr() >> kMapKeyShift));
41 : // Base the offset on a simple combination of name and map.
42 1134025 : uint32_t key = map_low32bits + field;
43 1134025 : 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 418286 : uint32_t name_low32bits = static_cast<uint32_t>(name.ptr());
52 418286 : uint32_t key = (seed - name_low32bits) + kSecondaryMagic;
53 418286 : 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 714311 : 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 714311 : Entry* primary = entry(primary_, primary_offset);
88 714311 : 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 714312 : if (old_handler != MaybeObject::FromObject(
93 1826976 : isolate_->builtins()->builtin(Builtins::kIllegal)) &&
94 398354 : primary->map != kNullAddress) {
95 398354 : Map old_map = Map::cast(Object(primary->map));
96 398354 : int seed = PrimaryOffset(Name::cast(Object(primary->key)), old_map);
97 : int secondary_offset =
98 : SecondaryOffset(Name::cast(Object(primary->key)), seed);
99 398354 : Entry* secondary = entry(secondary_, secondary_offset);
100 398354 : *secondary = *primary;
101 : }
102 :
103 : // Update primary cache.
104 714312 : primary->key = name.ptr();
105 714312 : primary->value = handler.ptr();
106 714312 : primary->map = map.ptr();
107 714312 : isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
108 714312 : }
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 988 : return MaybeObject(primary->value);
116 : }
117 : int secondary_offset = SecondaryOffset(name, primary_offset);
118 19492 : Entry* secondary = entry(secondary_, secondary_offset);
119 19492 : if (secondary->key == name.ptr() && secondary->map == map.ptr()) {
120 292 : return MaybeObject(secondary->value);
121 : }
122 19200 : return MaybeObject();
123 : }
124 :
125 270982 : void StubCache::Clear() {
126 : MaybeObject empty = MaybeObject::FromObject(
127 541964 : isolate_->builtins()->builtin(Builtins::kIllegal));
128 : Name empty_string = ReadOnlyRoots(isolate()).empty_string();
129 1108705926 : for (int i = 0; i < kPrimaryTableSize; i++) {
130 554217472 : primary_[i].key = empty_string.ptr();
131 554217472 : primary_[i].map = kNullAddress;
132 554217472 : primary_[i].value = empty.ptr();
133 : }
134 277526150 : for (int j = 0; j < kSecondaryTableSize; j++) {
135 138627584 : secondary_[j].key = empty_string.ptr();
136 138627584 : secondary_[j].map = kNullAddress;
137 138627584 : secondary_[j].value = empty.ptr();
138 : }
139 270982 : }
140 :
141 : } // namespace internal
142 120216 : } // namespace v8
|