| 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.
|
|
|