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 122101 : 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 122101 : }
21 :
22 122098 : void StubCache::Initialize() {
23 : DCHECK(base::bits::IsPowerOfTwo(kPrimaryTableSize));
24 : DCHECK(base::bits::IsPowerOfTwo(kSecondaryTableSize));
25 122098 : Clear();
26 122098 : }
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 1175271 : uint32_t map_low32bits = static_cast<uint32_t>(map.ptr());
40 : // Base the offset on a simple combination of name and map.
41 1175271 : uint32_t key = map_low32bits + field;
42 1175271 : 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 462343 : uint32_t name_low32bits = static_cast<uint32_t>(name.ptr());
51 462343 : uint32_t key = (seed - name_low32bits) + kSecondaryMagic;
52 462343 : return key & ((kSecondaryTableSize - 1) << kCacheIndexShift);
53 : }
54 :
55 880 : int StubCache::PrimaryOffsetForTesting(Name name, Map map) {
56 880 : return PrimaryOffset(name, map);
57 : }
58 :
59 440 : int StubCache::SecondaryOffsetForTesting(Name name, int seed) {
60 440 : 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::InYoungGeneration(name));
71 : DCHECK(!Heap::InYoungGeneration(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 1423080 : 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 711540 : Entry* primary = entry(primary_, primary_offset);
87 711540 : 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 711540 : if (old_handler != MaybeObject::FromObject(
92 1153911 : isolate_->builtins()->builtin(Builtins::kIllegal)) &&
93 442371 : primary->map != kNullAddress) {
94 442371 : Map old_map = Map::cast(Object(primary->map));
95 442371 : int seed = PrimaryOffset(Name::cast(Object(primary->key)), old_map);
96 : int secondary_offset =
97 442371 : SecondaryOffset(Name::cast(Object(primary->key)), seed);
98 442371 : Entry* secondary = entry(secondary_, secondary_offset);
99 442371 : *secondary = *primary;
100 : }
101 :
102 : // Update primary cache.
103 711540 : primary->key = name.ptr();
104 711540 : primary->value = handler.ptr();
105 711540 : primary->map = map.ptr();
106 711540 : isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
107 711540 : }
108 :
109 20480 : MaybeObject StubCache::Get(Name name, Map map) {
110 : DCHECK(CommonStubCacheChecks(this, name, map, MaybeObject()));
111 : int primary_offset = PrimaryOffset(name, map);
112 20480 : Entry* primary = entry(primary_, primary_offset);
113 20480 : if (primary->key == name.ptr() && primary->map == map.ptr()) {
114 948 : return MaybeObject(primary->value);
115 : }
116 : int secondary_offset = SecondaryOffset(name, primary_offset);
117 19532 : Entry* secondary = entry(secondary_, secondary_offset);
118 19532 : if (secondary->key == name.ptr() && secondary->map == map.ptr()) {
119 296 : return MaybeObject(secondary->value);
120 : }
121 19236 : return MaybeObject();
122 : }
123 :
124 542244 : void StubCache::Clear() {
125 : MaybeObject empty = MaybeObject::FromObject(
126 271122 : isolate_->builtins()->builtin(Builtins::kIllegal));
127 : Name empty_string = ReadOnlyRoots(isolate()).empty_string();
128 555526783 : for (int i = 0; i < kPrimaryTableSize; i++) {
129 555255661 : primary_[i].key = empty_string.ptr();
130 555255661 : primary_[i].map = kNullAddress;
131 555255661 : primary_[i].value = empty.ptr();
132 : }
133 138702848 : for (int j = 0; j < kSecondaryTableSize; j++) {
134 138702848 : secondary_[j].key = empty_string.ptr();
135 138702848 : secondary_[j].map = kNullAddress;
136 138702848 : secondary_[j].value = empty.ptr();
137 : }
138 271122 : }
139 :
140 : } // namespace internal
141 178779 : } // namespace v8
|