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 11 namespace v8 { 12 namespace 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 20 class SCTableReference { 21 public: address()22 Address address() const { return address_; } 23 24 private: SCTableReference(Address address)25 explicit SCTableReference(Address address) : address_(address) {} 26 27 Address address_; 28 29 friend class StubCache; 30 }; 31 32 class 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 key_reference(StubCache::Table table)54 SCTableReference key_reference(StubCache::Table table) { 55 return SCTableReference( 56 reinterpret_cast<Address>(&first_entry(table)->key)); 57 } 58 map_reference(StubCache::Table table)59 SCTableReference map_reference(StubCache::Table table) { 60 return SCTableReference( 61 reinterpret_cast<Address>(&first_entry(table)->map)); 62 } 63 value_reference(StubCache::Table table)64 SCTableReference value_reference(StubCache::Table table) { 65 return SCTableReference( 66 reinterpret_cast<Address>(&first_entry(table)->value)); 67 } 68 first_entry(StubCache::Table table)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 isolate()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. entry(Entry * table,int offset)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