Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index 292e75300633026409a114e52ddda7e0ceae7f48..edb2b111185b1742cbdc63a86549184ad5b6f913 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -2489,10 +2489,12 @@ MaybeObject* NormalizedMapCache::Get(JSObject* obj, |
PropertyNormalizationMode mode) { |
Isolate* isolate = obj->GetIsolate(); |
Map* fast = obj->map(); |
- int index = Hash(fast) % kEntries; |
+ int index = fast->Hash() % kEntries; |
Object* result = get(index); |
- if (result->IsMap() && CheckHit(Map::cast(result), fast, mode)) { |
+ if (result->IsMap() && |
+ Map::cast(result)->EquivalentToForNormalization(fast, mode)) { |
#ifdef DEBUG |
+ Map::cast(result)->SharedMapVerify(); |
if (FLAG_enable_slow_asserts) { |
// The cached map should match newly created normalized map bit-by-bit. |
Object* fresh; |
@@ -2528,43 +2530,6 @@ void NormalizedMapCache::Clear() { |
} |
-int NormalizedMapCache::Hash(Map* fast) { |
- // For performance reasons we only hash the 3 most variable fields of a map: |
- // constructor, prototype and bit_field2. |
- |
- // Shift away the tag. |
- int hash = (static_cast<uint32_t>( |
- reinterpret_cast<uintptr_t>(fast->constructor())) >> 2); |
- |
- // XOR-ing the prototype and constructor directly yields too many zero bits |
- // when the two pointers are close (which is fairly common). |
- // To avoid this we shift the prototype 4 bits relatively to the constructor. |
- hash ^= (static_cast<uint32_t>( |
- reinterpret_cast<uintptr_t>(fast->prototype())) << 2); |
- |
- return hash ^ (hash >> 16) ^ fast->bit_field2(); |
-} |
- |
- |
-bool NormalizedMapCache::CheckHit(Map* slow, |
- Map* fast, |
- PropertyNormalizationMode mode) { |
-#ifdef DEBUG |
- slow->SharedMapVerify(); |
-#endif |
- return |
- slow->constructor() == fast->constructor() && |
- slow->prototype() == fast->prototype() && |
- slow->inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ? |
- 0 : |
- fast->inobject_properties()) && |
- slow->instance_type() == fast->instance_type() && |
- slow->bit_field() == fast->bit_field() && |
- slow->bit_field2() == fast->bit_field2() && |
- (slow->bit_field3() & ~(1<<Map::kIsShared)) == fast->bit_field3(); |
-} |
- |
- |
MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) { |
if (map()->is_shared()) { |
// Fast case maps are never marked as shared. |
@@ -4058,6 +4023,7 @@ class CodeCacheHashTableKey : public HashTableKey { |
private: |
String* name_; |
Code::Flags flags_; |
+ // TODO(jkummerow): We should be able to get by without this. |
Code* code_; |
}; |
@@ -4077,7 +4043,7 @@ MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) { |
if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
} |
- // Don't use this, as the table might have grown. |
+ // Don't use |this|, as the table might have grown. |
CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj); |
int entry = cache->FindInsertionEntry(key.Hash()); |
@@ -4123,6 +4089,165 @@ static bool HasKey(FixedArray* array, Object* key) { |
} |
+MaybeObject* PolymorphicCodeCache::Update(MapList* maps, |
+ Code::Flags flags, |
+ Code* code) { |
+ // Initialize cache if necessary. |
+ if (cache()->IsUndefined()) { |
+ Object* result; |
+ { MaybeObject* maybe_result = |
+ PolymorphicCodeCacheHashTable::Allocate( |
+ PolymorphicCodeCacheHashTable::kInitialSize); |
+ if (!maybe_result->ToObject(&result)) return maybe_result; |
+ } |
+ set_cache(result); |
+ } else { |
+ // This entry shouldn't be contained in the cache yet. |
+ ASSERT(PolymorphicCodeCacheHashTable::cast(cache()) |
+ ->Lookup(maps, flags)->IsUndefined()); |
+ } |
+ PolymorphicCodeCacheHashTable* hash_table = |
+ PolymorphicCodeCacheHashTable::cast(cache()); |
+ Object* new_cache; |
+ { MaybeObject* maybe_new_cache = hash_table->Put(maps, flags, code); |
+ if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache; |
+ } |
+ set_cache(new_cache); |
+ return this; |
+} |
+ |
+ |
+Object* PolymorphicCodeCache::Lookup(MapList* maps, Code::Flags flags) { |
+ if (!cache()->IsUndefined()) { |
+ PolymorphicCodeCacheHashTable* hash_table = |
+ PolymorphicCodeCacheHashTable::cast(cache()); |
+ return hash_table->Lookup(maps, flags); |
+ } else { |
+ return GetHeap()->undefined_value(); |
+ } |
+} |
+ |
+ |
+// Despite their name, object of this class are not stored in the actual |
+// hash table; instead they're temporarily used for lookups. It is therefore |
+// safe to have a weak (non-owning) pointer to a MapList as a member field. |
+class PolymorphicCodeCacheHashTableKey : public HashTableKey { |
+ public: |
+ // Callers must ensure that |maps| outlives the newly constructed object. |
+ PolymorphicCodeCacheHashTableKey(MapList* maps, int code_flags) |
+ : maps_(maps), |
+ code_flags_(code_flags) {} |
+ |
+ bool IsMatch(Object* other) { |
+ MapList other_maps(kDefaultListAllocationSize); |
+ int other_flags; |
+ FromObject(other, &other_flags, &other_maps); |
+ if (code_flags_ != other_flags) return false; |
+ if (maps_->length() != other_maps.length()) return false; |
+ // Compare just the hashes first because it's faster. |
+ int this_hash = MapsHashHelper(maps_, code_flags_); |
+ int other_hash = MapsHashHelper(&other_maps, other_flags); |
+ if (this_hash != other_hash) return false; |
+ |
+ // Full comparison: for each map in maps_, look for an equivalent map in |
+ // other_maps. This implementation is slow, but probably good enough for |
+ // now because the lists are short (<= 4 elements currently). |
+ for (int i = 0; i < maps_->length(); ++i) { |
+ bool match_found = false; |
+ for (int j = 0; j < other_maps.length(); ++j) { |
+ if (maps_->at(i)->EquivalentTo(other_maps.at(j))) { |
+ match_found = true; |
+ break; |
+ } |
+ } |
+ if (!match_found) return false; |
+ } |
+ return true; |
+ } |
+ |
+ static uint32_t MapsHashHelper(MapList* maps, int code_flags) { |
+ uint32_t hash = code_flags; |
+ for (int i = 0; i < maps->length(); ++i) { |
+ hash ^= maps->at(i)->Hash(); |
+ } |
+ return hash; |
+ } |
+ |
+ uint32_t Hash() { |
+ return MapsHashHelper(maps_, code_flags_); |
+ } |
+ |
+ uint32_t HashForObject(Object* obj) { |
+ MapList other_maps(kDefaultListAllocationSize); |
+ int other_flags; |
+ FromObject(obj, &other_flags, &other_maps); |
+ return MapsHashHelper(&other_maps, other_flags); |
+ } |
+ |
+ MUST_USE_RESULT MaybeObject* AsObject() { |
+ Object* obj; |
+ // The maps in |maps_| must be copied to a newly allocated FixedArray, |
+ // both because the referenced MapList is short-lived, and because C++ |
+ // objects can't be stored in the heap anyway. |
+ { MaybeObject* maybe_obj = |
+ HEAP->AllocateUninitializedFixedArray(maps_->length() + 1); |
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
+ } |
+ FixedArray* list = FixedArray::cast(obj); |
+ list->set(0, Smi::FromInt(code_flags_)); |
+ for (int i = 0; i < maps_->length(); ++i) { |
+ list->set(i + 1, maps_->at(i)); |
+ } |
+ return list; |
+ } |
+ |
+ private: |
+ static MapList* FromObject(Object* obj, int* code_flags, MapList* maps) { |
+ FixedArray* list = FixedArray::cast(obj); |
+ maps->Rewind(0); |
+ *code_flags = Smi::cast(list->get(0))->value(); |
+ for (int i = 1; i < list->length(); ++i) { |
+ maps->Add(Map::cast(list->get(i))); |
+ } |
+ return maps; |
+ } |
+ |
+ MapList* maps_; // weak. |
+ int code_flags_; |
+ static const int kDefaultListAllocationSize = |
+ KeyedIC::kMaxKeyedPolymorphism + 1; |
+}; |
+ |
+ |
+Object* PolymorphicCodeCacheHashTable::Lookup(MapList* maps, int code_flags) { |
+ PolymorphicCodeCacheHashTableKey key(maps, code_flags); |
+ int entry = FindEntry(&key); |
+ if (entry == kNotFound) return GetHeap()->undefined_value(); |
+ return get(EntryToIndex(entry) + 1); |
+} |
+ |
+ |
+MaybeObject* PolymorphicCodeCacheHashTable::Put(MapList* maps, |
+ int code_flags, |
+ Code* code) { |
+ PolymorphicCodeCacheHashTableKey key(maps, code_flags); |
+ Object* obj; |
+ { MaybeObject* maybe_obj = EnsureCapacity(1, &key); |
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
+ } |
+ PolymorphicCodeCacheHashTable* cache = |
+ reinterpret_cast<PolymorphicCodeCacheHashTable*>(obj); |
+ int entry = cache->FindInsertionEntry(key.Hash()); |
+ { MaybeObject* maybe_obj = key.AsObject(); |
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
+ } |
+ cache->set(EntryToIndex(entry), obj); |
+ cache->set(EntryToIndex(entry) + 1, code); |
+ cache->ElementAdded(); |
+ return cache; |
+} |
+ |
+ |
MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) { |
ASSERT(!array->HasExternalArrayElements()); |
switch (array->GetElementsKind()) { |
@@ -5768,6 +5893,40 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { |
} |
+int Map::Hash() { |
+ // For performance reasons we only hash the 3 most variable fields of a map: |
+ // constructor, prototype and bit_field2. |
+ |
+ // Shift away the tag. |
+ int hash = (static_cast<uint32_t>( |
+ reinterpret_cast<uintptr_t>(constructor())) >> 2); |
+ |
+ // XOR-ing the prototype and constructor directly yields too many zero bits |
+ // when the two pointers are close (which is fairly common). |
+ // To avoid this we shift the prototype 4 bits relatively to the constructor. |
+ hash ^= (static_cast<uint32_t>( |
+ reinterpret_cast<uintptr_t>(prototype())) << 2); |
+ |
+ return hash ^ (hash >> 16) ^ bit_field2(); |
+} |
+ |
+ |
+bool Map::EquivalentToForNormalization(Map* other, |
+ PropertyNormalizationMode mode) { |
+ return |
+ constructor() == other->constructor() && |
+ prototype() == other->prototype() && |
+ inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ? |
+ 0 : |
+ other->inobject_properties()) && |
+ instance_type() == other->instance_type() && |
+ bit_field() == other->bit_field() && |
+ bit_field2() == other->bit_field2() && |
+ (bit_field3() & ~(1<<Map::kIsShared)) == |
+ (other->bit_field3() & ~(1<<Map::kIsShared)); |
+} |
+ |
+ |
void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) { |
// Iterate over all fields in the body but take care in dealing with |
// the code entry. |