xref: /third_party/node/deps/v8/src/ic/stub-cache.h (revision 1cb0ef41)
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#ifndef V8_IC_STUB_CACHE_H_
6#define V8_IC_STUB_CACHE_H_
7
8#include "src/objects/name.h"
9#include "src/objects/tagged-value.h"
10
11namespace v8 {
12namespace internal {
13
14// The stub cache is used for megamorphic property accesses.
15// It maps (map, name, type) to property access handlers. The cache does not
16// need explicit invalidation when a prototype chain is modified, since the
17// handlers verify the chain.
18
19
20class SCTableReference {
21 public:
22  Address address() const { return address_; }
23
24 private:
25  explicit SCTableReference(Address address) : address_(address) {}
26
27  Address address_;
28
29  friend class StubCache;
30};
31
32class V8_EXPORT_PRIVATE StubCache {
33 public:
34  struct Entry {
35    // {key} is a tagged Name pointer, may be cleared by setting to empty
36    // string.
37    StrongTaggedValue key;
38    // {value} is a tagged heap object reference (weak or strong), equivalent
39    // to a MaybeObject's payload.
40    TaggedValue value;
41    // {map} is a tagged Map pointer, may be cleared by setting to Smi::zero().
42    StrongTaggedValue map;
43  };
44
45  void Initialize();
46  // Access cache for entry hash(name, map).
47  void Set(Name name, Map map, MaybeObject handler);
48  MaybeObject Get(Name name, Map map);
49  // Clear the lookup table (@ mark compact collection).
50  void Clear();
51
52  enum Table { kPrimary, kSecondary };
53
54  SCTableReference key_reference(StubCache::Table table) {
55    return SCTableReference(
56        reinterpret_cast<Address>(&first_entry(table)->key));
57  }
58
59  SCTableReference map_reference(StubCache::Table table) {
60    return SCTableReference(
61        reinterpret_cast<Address>(&first_entry(table)->map));
62  }
63
64  SCTableReference value_reference(StubCache::Table table) {
65    return SCTableReference(
66        reinterpret_cast<Address>(&first_entry(table)->value));
67  }
68
69  StubCache::Entry* first_entry(StubCache::Table table) {
70    switch (table) {
71      case StubCache::kPrimary:
72        return StubCache::primary_;
73      case StubCache::kSecondary:
74        return StubCache::secondary_;
75    }
76    UNREACHABLE();
77  }
78
79  Isolate* isolate() { return isolate_; }
80
81  // Setting kCacheIndexShift to Name::HashBits::kShift is convenient because it
82  // causes the bit field inside the hash field to get shifted out implicitly.
83  // Note that kCacheIndexShift must not get too large, because
84  // sizeof(Entry) needs to be a multiple of 1 << kCacheIndexShift (see
85  // the STATIC_ASSERT below, in {entry(...)}).
86  static const int kCacheIndexShift = Name::HashBits::kShift;
87
88  static const int kPrimaryTableBits = 11;
89  static const int kPrimaryTableSize = (1 << kPrimaryTableBits);
90  static const int kSecondaryTableBits = 9;
91  static const int kSecondaryTableSize = (1 << kSecondaryTableBits);
92
93  // Used to introduce more entropy from the higher bits of the Map address.
94  // This should fill in the masked out kCacheIndexShift-bits.
95  static const int kMapKeyShift = kPrimaryTableBits + kCacheIndexShift;
96  static const int kSecondaryKeyShift = kSecondaryTableBits + kCacheIndexShift;
97
98  static int PrimaryOffsetForTesting(Name name, Map map);
99  static int SecondaryOffsetForTesting(Name name, Map map);
100
101  // The constructor is made public only for the purposes of testing.
102  explicit StubCache(Isolate* isolate);
103  StubCache(const StubCache&) = delete;
104  StubCache& operator=(const StubCache&) = delete;
105
106 private:
107  // The stub cache has a primary and secondary level.  The two levels have
108  // different hashing algorithms in order to avoid simultaneous collisions
109  // in both caches.  Unlike a probing strategy (quadratic or otherwise) the
110  // update strategy on updates is fairly clear and simple:  Any existing entry
111  // in the primary cache is moved to the secondary cache, and secondary cache
112  // entries are overwritten.
113
114  // Hash algorithm for the primary table.  This algorithm is replicated in
115  // assembler for every architecture.  Returns an index into the table that
116  // is scaled by 1 << kCacheIndexShift.
117  static int PrimaryOffset(Name name, Map map);
118
119  // Hash algorithm for the secondary table.  This algorithm is replicated in
120  // assembler for every architecture.  Returns an index into the table that
121  // is scaled by 1 << kCacheIndexShift.
122  static int SecondaryOffset(Name name, Map map);
123
124  // Compute the entry for a given offset in exactly the same way as
125  // we do in generated code.  We generate an hash code that already
126  // ends in Name::HashBits::kShift 0s.  Then we multiply it so it is a multiple
127  // of sizeof(Entry).  This makes it easier to avoid making mistakes
128  // in the hashed offset computations.
129  static Entry* entry(Entry* table, int offset) {
130    // The size of {Entry} must be a multiple of 1 << kCacheIndexShift.
131    STATIC_ASSERT((sizeof(*table) >> kCacheIndexShift) << kCacheIndexShift ==
132                  sizeof(*table));
133    const int multiplier = sizeof(*table) >> kCacheIndexShift;
134    return reinterpret_cast<Entry*>(reinterpret_cast<Address>(table) +
135                                    offset * multiplier);
136  }
137
138 private:
139  Entry primary_[kPrimaryTableSize];
140  Entry secondary_[kSecondaryTableSize];
141  Isolate* isolate_;
142
143  friend class Isolate;
144  friend class SCTableReference;
145};
146}  // namespace internal
147}  // namespace v8
148
149#endif  // V8_IC_STUB_CACHE_H_
150