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_STUB_CACHE_H_ 6 #define V8_STUB_CACHE_H_ 7 8 #include "src/macro-assembler.h" 9 10 namespace v8 { 11 namespace internal { 12 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 33 class StubCache { 34 public: 35 struct Entry { 36 Name* key; 37 Code* value; 38 Map* map; 39 }; 40 41 void Initialize(); 42 // Access cache for entry hash(name, map). 43 Code* Set(Name* name, Map* map, Code* code); 44 Code* Get(Name* name, Map* map, Code::Flags flags); 45 // Clear the lookup table (@ mark compact collection). 46 void Clear(); 47 // Collect all maps that match the name and flags. 48 void CollectMatchingMaps(SmallMapList* types, Handle<Name> name, 49 Code::Flags flags, Handle<Context> native_context, 50 Zone* zone); 51 // Generate code for probing the stub cache table. 52 // Arguments extra, extra2 and extra3 may be used to pass additional scratch 53 // registers. Set to no_reg if not needed. 54 // If leave_frame is true, then exit a frame before the tail call. 55 void GenerateProbe(MacroAssembler* masm, Code::Flags flags, bool leave_frame, 56 Register receiver, Register name, Register scratch, 57 Register extra, Register extra2 = no_reg, 58 Register extra3 = no_reg); 59 60 enum Table { kPrimary, kSecondary }; 61 key_reference(StubCache::Table table)62 SCTableReference key_reference(StubCache::Table table) { 63 return SCTableReference( 64 reinterpret_cast<Address>(&first_entry(table)->key)); 65 } 66 map_reference(StubCache::Table table)67 SCTableReference map_reference(StubCache::Table table) { 68 return SCTableReference( 69 reinterpret_cast<Address>(&first_entry(table)->map)); 70 } 71 value_reference(StubCache::Table table)72 SCTableReference value_reference(StubCache::Table table) { 73 return SCTableReference( 74 reinterpret_cast<Address>(&first_entry(table)->value)); 75 } 76 first_entry(StubCache::Table table)77 StubCache::Entry* first_entry(StubCache::Table table) { 78 switch (table) { 79 case StubCache::kPrimary: 80 return StubCache::primary_; 81 case StubCache::kSecondary: 82 return StubCache::secondary_; 83 } 84 UNREACHABLE(); 85 return NULL; 86 } 87 isolate()88 Isolate* isolate() { return isolate_; } 89 90 // Setting the entry size such that the index is shifted by Name::kHashShift 91 // is convenient; shifting down the length field (to extract the hash code) 92 // automatically discards the hash bit field. 93 static const int kCacheIndexShift = Name::kHashShift; 94 95 private: 96 explicit StubCache(Isolate* isolate); 97 98 // The stub cache has a primary and secondary level. The two levels have 99 // different hashing algorithms in order to avoid simultaneous collisions 100 // in both caches. Unlike a probing strategy (quadratic or otherwise) the 101 // update strategy on updates is fairly clear and simple: Any existing entry 102 // in the primary cache is moved to the secondary cache, and secondary cache 103 // entries are overwritten. 104 105 // Hash algorithm for the primary table. This algorithm is replicated in 106 // assembler for every architecture. Returns an index into the table that 107 // is scaled by 1 << kCacheIndexShift. PrimaryOffset(Name * name,Code::Flags flags,Map * map)108 static int PrimaryOffset(Name* name, Code::Flags flags, Map* map) { 109 STATIC_ASSERT(kCacheIndexShift == Name::kHashShift); 110 // Compute the hash of the name (use entire hash field). 111 DCHECK(name->HasHashCode()); 112 uint32_t field = name->hash_field(); 113 // Using only the low bits in 64-bit mode is unlikely to increase the 114 // risk of collision even if the heap is spread over an area larger than 115 // 4Gb (and not at all if it isn't). 116 uint32_t map_low32bits = 117 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map)); 118 // We always set the in_loop bit to zero when generating the lookup code 119 // so do it here too so the hash codes match. 120 uint32_t iflags = 121 (static_cast<uint32_t>(flags) & ~Code::kFlagsNotUsedInLookup); 122 // Base the offset on a simple combination of name, flags, and map. 123 uint32_t key = (map_low32bits + field) ^ iflags; 124 return key & ((kPrimaryTableSize - 1) << kCacheIndexShift); 125 } 126 127 // Hash algorithm for the secondary table. This algorithm is replicated in 128 // assembler for every architecture. Returns an index into the table that 129 // is scaled by 1 << kCacheIndexShift. SecondaryOffset(Name * name,Code::Flags flags,int seed)130 static int SecondaryOffset(Name* name, Code::Flags flags, int seed) { 131 // Use the seed from the primary cache in the secondary cache. 132 uint32_t name_low32bits = 133 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name)); 134 // We always set the in_loop bit to zero when generating the lookup code 135 // so do it here too so the hash codes match. 136 uint32_t iflags = 137 (static_cast<uint32_t>(flags) & ~Code::kFlagsNotUsedInLookup); 138 uint32_t key = (seed - name_low32bits) + iflags; 139 return key & ((kSecondaryTableSize - 1) << kCacheIndexShift); 140 } 141 142 // Compute the entry for a given offset in exactly the same way as 143 // we do in generated code. We generate an hash code that already 144 // ends in Name::kHashShift 0s. Then we multiply it so it is a multiple 145 // of sizeof(Entry). This makes it easier to avoid making mistakes 146 // in the hashed offset computations. entry(Entry * table,int offset)147 static Entry* entry(Entry* table, int offset) { 148 const int multiplier = sizeof(*table) >> Name::kHashShift; 149 return reinterpret_cast<Entry*>(reinterpret_cast<Address>(table) + 150 offset * multiplier); 151 } 152 153 static const int kPrimaryTableBits = 11; 154 static const int kPrimaryTableSize = (1 << kPrimaryTableBits); 155 static const int kSecondaryTableBits = 9; 156 static const int kSecondaryTableSize = (1 << kSecondaryTableBits); 157 158 private: 159 Entry primary_[kPrimaryTableSize]; 160 Entry secondary_[kSecondaryTableSize]; 161 Isolate* isolate_; 162 163 friend class Isolate; 164 friend class SCTableReference; 165 166 DISALLOW_COPY_AND_ASSIGN(StubCache); 167 }; 168 } 169 } // namespace v8::internal 170 171 #endif // V8_STUB_CACHE_H_ 172