Commit 82726b64 authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[hashtable] Allow GlobalDictionary to have holes

Rather than marking deleted GlobalDictionary entries with a "The Hole"
valued PropertyCell, we now remove those PropertyCells entirely and
use the standard HashTable deleted item marker (also the Hole).

This comes with several simplifications:

  1) We no longer need a customizable IsKey method on HastTable shapes,
     which was only used by GlobalDictionary to mark "The Hole" cells
     as not real keys,
  2) We can get rid of IsLive/IsKey from the Shape entirely, and define
     it directly in the HashTable, which will also allow us (in the
     future) to encourage caching of "undefined" and "Hole" where used
     for IsKey checks,
  3) PropertyCell invalidation doesn't necessarily have to allocate a
     new replacement cell (specifically, on deletion), nor does it have
     to deal with cells that contain the Hole,
  4) kNeedsHoleCheck is renamed to kMatchNeedsHoleCheck (to be explicit
     that this is only needed to guard IsMatch, which may do an
     indentity comparison and thus not need the HoleCheck guard). It's
     also moved out of BaseShape and into the various shapes that
     define IsMatch, to make them more explicitly think about the
     value,
  5) Modified some while loops into for loops to allow clearer use of
     "continue" on successful hole checks.

Change-Id: If591cbb6b49d59726bdc615413aba4f78fd64632
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2292230
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68807}
parent dc82799d
...@@ -7557,6 +7557,7 @@ void CodeStubAssembler::NameDictionaryLookup( ...@@ -7557,6 +7557,7 @@ void CodeStubAssembler::NameDictionaryLookup(
Goto(&loop); Goto(&loop);
BIND(&loop); BIND(&loop);
{ {
Label next_probe(this);
TNode<IntPtrT> entry = var_entry.value(); TNode<IntPtrT> entry = var_entry.value();
TNode<IntPtrT> index = EntryToIndex<Dictionary>(entry); TNode<IntPtrT> index = EntryToIndex<Dictionary>(entry);
...@@ -7566,13 +7567,18 @@ void CodeStubAssembler::NameDictionaryLookup( ...@@ -7566,13 +7567,18 @@ void CodeStubAssembler::NameDictionaryLookup(
CAST(UnsafeLoadFixedArrayElement(dictionary, index)); CAST(UnsafeLoadFixedArrayElement(dictionary, index));
GotoIf(TaggedEqual(current, undefined), if_not_found); GotoIf(TaggedEqual(current, undefined), if_not_found);
if (mode == kFindExisting) { if (mode == kFindExisting) {
if (Dictionary::ShapeT::kMatchNeedsHoleCheck) {
GotoIf(TaggedEqual(current, TheHoleConstant()), &next_probe);
}
current = LoadName<Dictionary>(current); current = LoadName<Dictionary>(current);
GotoIf(TaggedEqual(current, unique_name), if_found); GotoIf(TaggedEqual(current, unique_name), if_found);
} else { } else {
DCHECK_EQ(kFindInsertionIndex, mode); DCHECK_EQ(kFindInsertionIndex, mode);
GotoIf(TaggedEqual(current, TheHoleConstant()), if_not_found); GotoIf(TaggedEqual(current, TheHoleConstant()), if_not_found);
} }
Goto(&next_probe);
BIND(&next_probe);
// See Dictionary::NextProbe(). // See Dictionary::NextProbe().
Increment(&var_count); Increment(&var_count);
entry = Signed(WordAnd(IntPtrAdd(entry, var_count.value()), mask)); entry = Signed(WordAnd(IntPtrAdd(entry, var_count.value()), mask));
......
...@@ -38,6 +38,7 @@ class CompilationCacheShape : public BaseShape<HashTableKey*> { ...@@ -38,6 +38,7 @@ class CompilationCacheShape : public BaseShape<HashTableKey*> {
static const int kPrefixSize = 0; static const int kPrefixSize = 0;
static const int kEntrySize = 3; static const int kEntrySize = 3;
static const bool kMatchNeedsHoleCheck = true;
}; };
class InfoCellPair { class InfoCellPair {
......
...@@ -205,15 +205,6 @@ PropertyCell GlobalDictionary::CellAt(const Isolate* isolate, ...@@ -205,15 +205,6 @@ PropertyCell GlobalDictionary::CellAt(const Isolate* isolate,
return PropertyCell::cast(KeyAt(isolate, entry)); return PropertyCell::cast(KeyAt(isolate, entry));
} }
bool GlobalDictionaryShape::IsLive(ReadOnlyRoots roots, Object k) {
DCHECK_NE(roots.the_hole_value(), k);
return k != roots.undefined_value();
}
bool GlobalDictionaryShape::IsKey(ReadOnlyRoots roots, Object k) {
return IsLive(roots, k) && !PropertyCell::cast(k).value().IsTheHole(roots);
}
Name GlobalDictionary::NameAt(InternalIndex entry) { Name GlobalDictionary::NameAt(InternalIndex entry) {
const Isolate* isolate = GetIsolateForPtrCompr(*this); const Isolate* isolate = GetIsolateForPtrCompr(*this);
return NameAt(isolate, entry); return NameAt(isolate, entry);
...@@ -239,6 +230,11 @@ void GlobalDictionary::SetEntry(InternalIndex entry, Object key, Object value, ...@@ -239,6 +230,11 @@ void GlobalDictionary::SetEntry(InternalIndex entry, Object key, Object value,
DetailsAtPut(entry, details); DetailsAtPut(entry, details);
} }
void GlobalDictionary::ClearEntry(InternalIndex entry) {
Object the_hole = this->GetReadOnlyRoots().the_hole_value();
set(EntryToIndex(entry) + kEntryKeyIndex, the_hole);
}
void GlobalDictionary::ValueAtPut(InternalIndex entry, Object value) { void GlobalDictionary::ValueAtPut(InternalIndex entry, Object value) {
set(EntryToIndex(entry), value); set(EntryToIndex(entry), value);
} }
......
...@@ -117,7 +117,7 @@ class NameDictionaryShape : public BaseDictionaryShape<Handle<Name>> { ...@@ -117,7 +117,7 @@ class NameDictionaryShape : public BaseDictionaryShape<Handle<Name>> {
static const int kPrefixSize = 2; static const int kPrefixSize = 2;
static const int kEntrySize = 3; static const int kEntrySize = 3;
static const int kEntryValueIndex = 1; static const int kEntryValueIndex = 1;
static const bool kNeedsHoleCheck = false; static const bool kMatchNeedsHoleCheck = false;
}; };
template <typename Derived, typename Shape> template <typename Derived, typename Shape>
...@@ -212,6 +212,7 @@ class V8_EXPORT_PRIVATE GlobalDictionaryShape : public NameDictionaryShape { ...@@ -212,6 +212,7 @@ class V8_EXPORT_PRIVATE GlobalDictionaryShape : public NameDictionaryShape {
static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object); static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object);
static const int kEntrySize = 1; // Overrides NameDictionaryShape::kEntrySize static const int kEntrySize = 1; // Overrides NameDictionaryShape::kEntrySize
static const bool kMatchNeedsHoleCheck = true;
template <typename Dictionary> template <typename Dictionary>
static inline PropertyDetails DetailsAt(Dictionary dict, InternalIndex entry); static inline PropertyDetails DetailsAt(Dictionary dict, InternalIndex entry);
...@@ -221,8 +222,6 @@ class V8_EXPORT_PRIVATE GlobalDictionaryShape : public NameDictionaryShape { ...@@ -221,8 +222,6 @@ class V8_EXPORT_PRIVATE GlobalDictionaryShape : public NameDictionaryShape {
PropertyDetails value); PropertyDetails value);
static inline Object Unwrap(Object key); static inline Object Unwrap(Object key);
static inline bool IsKey(ReadOnlyRoots roots, Object k);
static inline bool IsLive(ReadOnlyRoots roots, Object key);
}; };
EXTERN_DECLARE_BASE_NAME_DICTIONARY(GlobalDictionary, GlobalDictionaryShape) EXTERN_DECLARE_BASE_NAME_DICTIONARY(GlobalDictionary, GlobalDictionaryShape)
...@@ -240,6 +239,7 @@ class V8_EXPORT_PRIVATE GlobalDictionary ...@@ -240,6 +239,7 @@ class V8_EXPORT_PRIVATE GlobalDictionary
inline PropertyCell CellAt(const Isolate* isolate, InternalIndex entry); inline PropertyCell CellAt(const Isolate* isolate, InternalIndex entry);
inline void SetEntry(InternalIndex entry, Object key, Object value, inline void SetEntry(InternalIndex entry, Object key, Object value,
PropertyDetails details); PropertyDetails details);
inline void ClearEntry(InternalIndex entry);
inline Name NameAt(InternalIndex entry); inline Name NameAt(InternalIndex entry);
inline Name NameAt(const Isolate* isolate, InternalIndex entry); inline Name NameAt(const Isolate* isolate, InternalIndex entry);
inline void ValueAtPut(InternalIndex entry, Object value); inline void ValueAtPut(InternalIndex entry, Object value);
...@@ -258,6 +258,8 @@ class NumberDictionaryBaseShape : public BaseDictionaryShape<uint32_t> { ...@@ -258,6 +258,8 @@ class NumberDictionaryBaseShape : public BaseDictionaryShape<uint32_t> {
static inline uint32_t Hash(ReadOnlyRoots roots, uint32_t key); static inline uint32_t Hash(ReadOnlyRoots roots, uint32_t key);
static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object); static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object);
static const bool kMatchNeedsHoleCheck = true;
}; };
class NumberDictionaryShape : public NumberDictionaryBaseShape { class NumberDictionaryShape : public NumberDictionaryBaseShape {
......
...@@ -147,25 +147,30 @@ InternalIndex HashTable<Derived, Shape>::FindEntry(const LocalIsolate* isolate, ...@@ -147,25 +147,30 @@ InternalIndex HashTable<Derived, Shape>::FindEntry(const LocalIsolate* isolate,
ReadOnlyRoots roots, Key key, ReadOnlyRoots roots, Key key,
int32_t hash) { int32_t hash) {
uint32_t capacity = Capacity(); uint32_t capacity = Capacity();
InternalIndex entry = FirstProbe(hash, capacity);
uint32_t count = 1; uint32_t count = 1;
// EnsureCapacity will guarantee the hash table is never full.
Object undefined = roots.undefined_value(); Object undefined = roots.undefined_value();
Object the_hole = roots.the_hole_value(); Object the_hole = roots.the_hole_value();
USE(the_hole); USE(the_hole);
while (true) { // EnsureCapacity will guarantee the hash table is never full.
for (InternalIndex entry = FirstProbe(hash, capacity);;
entry = NextProbe(entry, count++, capacity)) {
Object element = KeyAt(isolate, entry); Object element = KeyAt(isolate, entry);
// Empty entry. Uses raw unchecked accessors because it is called by the // Empty entry. Uses raw unchecked accessors because it is called by the
// string table during bootstrapping. // string table during bootstrapping.
if (element == undefined) break; if (element == undefined) break;
if (!(Shape::kNeedsHoleCheck && the_hole == element)) { if (Shape::kMatchNeedsHoleCheck && element == the_hole) continue;
if (Shape::IsMatch(key, element)) return entry; if (Shape::IsMatch(key, element)) return entry;
}
entry = NextProbe(entry, count++, capacity);
} }
return InternalIndex::NotFound(); return InternalIndex::NotFound();
} }
// static
template <typename Derived, typename Shape>
bool HashTable<Derived, Shape>::IsKey(ReadOnlyRoots roots, Object k) {
// TODO(leszeks): Dictionaries that don't delete could skip the hole check.
return k != roots.undefined_value() && k != roots.the_hole_value();
}
template <typename Derived, typename Shape> template <typename Derived, typename Shape>
bool HashTable<Derived, Shape>::ToKey(ReadOnlyRoots roots, InternalIndex entry, bool HashTable<Derived, Shape>::ToKey(ReadOnlyRoots roots, InternalIndex entry,
Object* out_k) { Object* out_k) {
...@@ -221,16 +226,6 @@ void HashTable<Derived, Shape>::SetCapacity(int capacity) { ...@@ -221,16 +226,6 @@ void HashTable<Derived, Shape>::SetCapacity(int capacity) {
set(kCapacityIndex, Smi::FromInt(capacity)); set(kCapacityIndex, Smi::FromInt(capacity));
} }
template <typename KeyT>
bool BaseShape<KeyT>::IsKey(ReadOnlyRoots roots, Object key) {
return IsLive(roots, key);
}
template <typename KeyT>
bool BaseShape<KeyT>::IsLive(ReadOnlyRoots roots, Object k) {
return k != roots.the_hole_value() && k != roots.undefined_value();
}
bool ObjectHashSet::Has(Isolate* isolate, Handle<Object> key, int32_t hash) { bool ObjectHashSet::Has(Isolate* isolate, Handle<Object> key, int32_t hash) {
return FindEntry(isolate, ReadOnlyRoots(isolate), key, hash).is_found(); return FindEntry(isolate, ReadOnlyRoots(isolate), key, hash).is_found();
} }
......
...@@ -50,7 +50,7 @@ namespace internal { ...@@ -50,7 +50,7 @@ namespace internal {
// static const int kEntrySize = ..; // static const int kEntrySize = ..;
// // Indicates whether IsMatch can deal with other being the_hole (a // // Indicates whether IsMatch can deal with other being the_hole (a
// // deleted entry). // // deleted entry).
// static const bool kNeedsHoleCheck = ..; // static const bool kMatchNeedsHoleCheck = ..;
// }; // };
// The prefix size indicates an amount of memory in the // The prefix size indicates an amount of memory in the
// beginning of the backing storage that can be used for non-element // beginning of the backing storage that can be used for non-element
...@@ -60,10 +60,7 @@ template <typename KeyT> ...@@ -60,10 +60,7 @@ template <typename KeyT>
class V8_EXPORT_PRIVATE BaseShape { class V8_EXPORT_PRIVATE BaseShape {
public: public:
using Key = KeyT; using Key = KeyT;
static const bool kNeedsHoleCheck = true;
static Object Unwrap(Object key) { return key; } static Object Unwrap(Object key) { return key; }
static inline bool IsKey(ReadOnlyRoots roots, Object key);
static inline bool IsLive(ReadOnlyRoots roots, Object key);
}; };
class V8_EXPORT_PRIVATE HashTableBase : public NON_EXPORTED_BASE(FixedArray) { class V8_EXPORT_PRIVATE HashTableBase : public NON_EXPORTED_BASE(FixedArray) {
...@@ -152,9 +149,7 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable ...@@ -152,9 +149,7 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable
// Returns whether k is a real key. The hole and undefined are not allowed as // Returns whether k is a real key. The hole and undefined are not allowed as
// keys and can be used to indicate missing or deleted elements. // keys and can be used to indicate missing or deleted elements.
static bool IsKey(ReadOnlyRoots roots, Object k) { static inline bool IsKey(ReadOnlyRoots roots, Object k);
return Shape::IsKey(roots, k);
}
inline bool ToKey(ReadOnlyRoots roots, InternalIndex entry, Object* out_k); inline bool ToKey(ReadOnlyRoots roots, InternalIndex entry, Object* out_k);
inline bool ToKey(const Isolate* isolate, InternalIndex entry, Object* out_k); inline bool ToKey(const Isolate* isolate, InternalIndex entry, Object* out_k);
...@@ -303,7 +298,7 @@ class ObjectHashTableShape : public BaseShape<Handle<Object>> { ...@@ -303,7 +298,7 @@ class ObjectHashTableShape : public BaseShape<Handle<Object>> {
static const int kPrefixSize = 0; static const int kPrefixSize = 0;
static const int kEntryValueIndex = 1; static const int kEntryValueIndex = 1;
static const int kEntrySize = 2; static const int kEntrySize = 2;
static const bool kNeedsHoleCheck = false; static const bool kMatchNeedsHoleCheck = false;
}; };
template <typename Derived, typename Shape> template <typename Derived, typename Shape>
......
...@@ -705,14 +705,18 @@ void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object, ...@@ -705,14 +705,18 @@ void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object,
DCHECK(entry.is_found()); DCHECK(entry.is_found());
if (object->IsJSGlobalObject()) { if (object->IsJSGlobalObject()) {
// If we have a global object, invalidate the cell and swap in a new one. // If we have a global object, invalidate the cell and remove it from the
// global object's dictionary.
Handle<GlobalDictionary> dictionary( Handle<GlobalDictionary> dictionary(
JSGlobalObject::cast(*object).global_dictionary(), isolate); JSGlobalObject::cast(*object).global_dictionary(), isolate);
auto cell = PropertyCell::InvalidateEntry(isolate, dictionary, entry); Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
cell->set_value(ReadOnlyRoots(isolate).the_hole_value());
cell->set_property_details( Handle<GlobalDictionary> new_dictionary =
PropertyDetails::Empty(PropertyCellType::kUninitialized)); GlobalDictionary::DeleteEntry(isolate, dictionary, entry);
JSGlobalObject::cast(*object).set_global_dictionary(*new_dictionary);
cell->ClearAndInvalidate(ReadOnlyRoots(isolate));
} else { } else {
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate); Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
...@@ -5675,38 +5679,8 @@ void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global, ...@@ -5675,38 +5679,8 @@ void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global,
auto dictionary = handle(global->global_dictionary(), global->GetIsolate()); auto dictionary = handle(global->global_dictionary(), global->GetIsolate());
InternalIndex entry = dictionary->FindEntry(global->GetIsolate(), name); InternalIndex entry = dictionary->FindEntry(global->GetIsolate(), name);
if (entry.is_not_found()) return; if (entry.is_not_found()) return;
PropertyCell::InvalidateEntry(global->GetIsolate(), dictionary, entry); PropertyCell::InvalidateAndReplaceEntry(global->GetIsolate(), dictionary,
} entry);
Handle<PropertyCell> JSGlobalObject::EnsureEmptyPropertyCell(
Handle<JSGlobalObject> global, Handle<Name> name,
PropertyCellType cell_type, InternalIndex* entry_out) {
Isolate* isolate = global->GetIsolate();
DCHECK(!global->HasFastProperties());
Handle<GlobalDictionary> dictionary(global->global_dictionary(), isolate);
InternalIndex entry = dictionary->FindEntry(isolate, name);
Handle<PropertyCell> cell;
if (entry.is_found()) {
if (entry_out) *entry_out = entry;
cell = handle(dictionary->CellAt(entry), isolate);
PropertyCellType original_cell_type = cell->property_details().cell_type();
DCHECK(original_cell_type == PropertyCellType::kInvalidated ||
original_cell_type == PropertyCellType::kUninitialized);
DCHECK(cell->value().IsTheHole(isolate));
if (original_cell_type == PropertyCellType::kInvalidated) {
cell = PropertyCell::InvalidateEntry(isolate, dictionary, entry);
}
PropertyDetails details(kData, NONE, cell_type);
cell->set_property_details(details);
return cell;
}
cell = isolate->factory()->NewPropertyCell(name);
PropertyDetails details(kData, NONE, cell_type);
dictionary = GlobalDictionary::Add(isolate, dictionary, name, cell, details,
entry_out);
// {*entry_out} is initialized inside GlobalDictionary::Add().
global->SetProperties(*dictionary);
return cell;
} }
// static // static
......
...@@ -1232,10 +1232,6 @@ class JSGlobalObject : public JSSpecialObject { ...@@ -1232,10 +1232,6 @@ class JSGlobalObject : public JSSpecialObject {
static void InvalidatePropertyCell(Handle<JSGlobalObject> object, static void InvalidatePropertyCell(Handle<JSGlobalObject> object,
Handle<Name> name); Handle<Name> name);
// Ensure that the global object has a cell for the given property name.
static Handle<PropertyCell> EnsureEmptyPropertyCell(
Handle<JSGlobalObject> global, Handle<Name> name,
PropertyCellType cell_type, InternalIndex* entry_out = nullptr);
DECL_CAST(JSGlobalObject) DECL_CAST(JSGlobalObject)
......
...@@ -552,23 +552,23 @@ void LookupIterator::PrepareTransitionToDataProperty( ...@@ -552,23 +552,23 @@ void LookupIterator::PrepareTransitionToDataProperty(
if (map->IsJSGlobalObjectMap()) { if (map->IsJSGlobalObjectMap()) {
// Install a property cell. // Install a property cell.
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver); Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell( DCHECK(!global->HasFastProperties());
global, name(), PropertyCellType::kUninitialized, &number_);
Handle<GlobalDictionary> dictionary(global->global_dictionary(isolate_), Handle<GlobalDictionary> dictionary(global->global_dictionary(isolate_),
isolate_); isolate_);
Handle<PropertyCell> cell = isolate_->factory()->NewPropertyCell(name());
DCHECK(cell->value(isolate_).IsTheHole(isolate_)); DCHECK(cell->value(isolate_).IsTheHole(isolate_));
DCHECK(!value->IsTheHole(isolate_)); DCHECK(!value->IsTheHole(isolate_));
transition_ = cell;
// Assign an enumeration index to the property and update
// SetNextEnumerationIndex.
int index = GlobalDictionary::NextEnumerationIndex(isolate_, dictionary);
dictionary->set_next_enumeration_index(index + 1);
property_details_ = PropertyDetails( property_details_ = PropertyDetails(
kData, attributes, PropertyCellType::kUninitialized, index); kData, attributes,
PropertyCellType new_type = PropertyCell::TypeForUninitializedCell(isolate_, value));
PropertyCell::UpdatedType(isolate(), cell, value, property_details_);
property_details_ = property_details_.set_cell_type(new_type); dictionary = GlobalDictionary::Add(isolate_, dictionary, name(), cell,
cell->set_property_details(property_details_); property_details_, &number_);
global->set_global_dictionary(*dictionary);
transition_ = cell;
has_property_ = true; has_property_ = true;
} else { } else {
// Don't set enumeration index (it will be set during value store). // Don't set enumeration index (it will be set during value store).
......
...@@ -6668,7 +6668,7 @@ void HashTable<Derived, Shape>::Rehash(const Isolate* isolate, ...@@ -6668,7 +6668,7 @@ void HashTable<Derived, Shape>::Rehash(const Isolate* isolate,
for (InternalIndex i : this->IterateEntries()) { for (InternalIndex i : this->IterateEntries()) {
uint32_t from_index = EntryToIndex(i); uint32_t from_index = EntryToIndex(i);
Object k = this->get(isolate, from_index); Object k = this->get(isolate, from_index);
if (!Shape::IsLive(roots, k)) continue; if (!IsKey(roots, k)) continue;
uint32_t hash = Shape::HashForObject(roots, k); uint32_t hash = Shape::HashForObject(roots, k);
uint32_t insertion_index = uint32_t insertion_index =
EntryToIndex(new_table.FindInsertionEntry(isolate, roots, hash)); EntryToIndex(new_table.FindInsertionEntry(isolate, roots, hash));
...@@ -6729,7 +6729,7 @@ void HashTable<Derived, Shape>::Rehash(const Isolate* isolate) { ...@@ -6729,7 +6729,7 @@ void HashTable<Derived, Shape>::Rehash(const Isolate* isolate) {
for (InternalIndex current(0); current.raw_value() < capacity; for (InternalIndex current(0); current.raw_value() < capacity;
/* {current} is advanced manually below, when appropriate.*/) { /* {current} is advanced manually below, when appropriate.*/) {
Object current_key = KeyAt(isolate, current); Object current_key = KeyAt(isolate, current);
if (!Shape::IsLive(roots, current_key)) { if (!IsKey(roots, current_key)) {
++current; // Advance to next entry. ++current; // Advance to next entry.
continue; continue;
} }
...@@ -6739,7 +6739,7 @@ void HashTable<Derived, Shape>::Rehash(const Isolate* isolate) { ...@@ -6739,7 +6739,7 @@ void HashTable<Derived, Shape>::Rehash(const Isolate* isolate) {
continue; continue;
} }
Object target_key = KeyAt(isolate, target); Object target_key = KeyAt(isolate, target);
if (!Shape::IsLive(roots, target_key) || if (!IsKey(roots, target_key) ||
EntryForProbe(roots, target_key, probe, target) != target) { EntryForProbe(roots, target_key, probe, target) != target) {
// Put the current element into the correct position. // Put the current element into the correct position.
Swap(current, target, mode); Swap(current, target, mode);
...@@ -6839,14 +6839,12 @@ template <typename Derived, typename Shape> ...@@ -6839,14 +6839,12 @@ template <typename Derived, typename Shape>
InternalIndex HashTable<Derived, Shape>::FindInsertionEntry( InternalIndex HashTable<Derived, Shape>::FindInsertionEntry(
const Isolate* isolate, ReadOnlyRoots roots, uint32_t hash) { const Isolate* isolate, ReadOnlyRoots roots, uint32_t hash) {
uint32_t capacity = Capacity(); uint32_t capacity = Capacity();
InternalIndex entry = FirstProbe(hash, capacity);
uint32_t count = 1; uint32_t count = 1;
// EnsureCapacity will guarantee the hash table is never full. // EnsureCapacity will guarantee the hash table is never full.
while (true) { for (InternalIndex entry = FirstProbe(hash, capacity);;
if (!Shape::IsLive(roots, KeyAt(isolate, entry))) break; entry = NextProbe(entry, count++, capacity)) {
entry = NextProbe(entry, count++, capacity); if (!IsKey(roots, KeyAt(isolate, entry))) return entry;
} }
return entry;
} }
template <typename Derived, typename Shape> template <typename Derived, typename Shape>
...@@ -8015,32 +8013,36 @@ Handle<JSArray> JSWeakCollection::GetEntries(Handle<JSWeakCollection> holder, ...@@ -8015,32 +8013,36 @@ Handle<JSArray> JSWeakCollection::GetEntries(Handle<JSWeakCollection> holder,
return isolate->factory()->NewJSArrayWithElements(entries); return isolate->factory()->NewJSArrayWithElements(entries);
} }
Handle<PropertyCell> PropertyCell::InvalidateEntry( void PropertyCell::ClearAndInvalidate(ReadOnlyRoots roots) {
// Cell is officially mutable henceforth.
DCHECK(!value().IsTheHole(roots));
PropertyDetails details = property_details();
details = details.set_cell_type(PropertyCellType::kInvalidated);
set_value(roots.the_hole_value());
set_property_details(details);
dependent_code().DeoptimizeDependentCodeGroup(
DependentCode::kPropertyCellChangedGroup);
}
// static
Handle<PropertyCell> PropertyCell::InvalidateAndReplaceEntry(
Isolate* isolate, Handle<GlobalDictionary> dictionary, Isolate* isolate, Handle<GlobalDictionary> dictionary,
InternalIndex entry) { InternalIndex entry) {
// Swap with a copy.
Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate); Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate);
Handle<Name> name(cell->name(), isolate); Handle<Name> name(cell->name(), isolate);
PropertyDetails details = cell->property_details();
DCHECK(details.IsConfigurable());
DCHECK(!cell->value().IsTheHole(isolate));
// Swap with a copy.
Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(name); Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(name);
new_cell->set_value(cell->value()); new_cell->set_value(cell->value());
dictionary->ValueAtPut(entry, *new_cell);
bool is_the_hole = cell->value().IsTheHole(isolate);
// Cell is officially mutable henceforth. // Cell is officially mutable henceforth.
PropertyDetails details = cell->property_details(); details = details.set_cell_type(PropertyCellType::kMutable);
DCHECK(details.IsConfigurable());
details = details.set_cell_type(is_the_hole ? PropertyCellType::kUninitialized
: PropertyCellType::kMutable);
new_cell->set_property_details(details); new_cell->set_property_details(details);
// Old cell is ready for invalidation. dictionary->ValueAtPut(entry, *new_cell);
if (is_the_hole) {
cell->set_value(ReadOnlyRoots(isolate).undefined_value()); cell->ClearAndInvalidate(ReadOnlyRoots(isolate));
} else {
cell->set_value(ReadOnlyRoots(isolate).the_hole_value());
}
details = details.set_cell_type(PropertyCellType::kInvalidated);
cell->set_property_details(details);
cell->dependent_code().DeoptimizeDependentCodeGroup(
DependentCode::kPropertyCellChangedGroup);
return new_cell; return new_cell;
} }
...@@ -8062,6 +8064,14 @@ static bool RemainsConstantType(Handle<PropertyCell> cell, ...@@ -8062,6 +8064,14 @@ static bool RemainsConstantType(Handle<PropertyCell> cell,
return false; return false;
} }
// static
PropertyCellType PropertyCell::TypeForUninitializedCell(Isolate* isolate,
Handle<Object> value) {
if (value->IsUndefined(isolate)) return PropertyCellType::kUndefined;
return PropertyCellType::kConstant;
}
// static
PropertyCellType PropertyCell::UpdatedType(Isolate* isolate, PropertyCellType PropertyCell::UpdatedType(Isolate* isolate,
Handle<PropertyCell> cell, Handle<PropertyCell> cell,
Handle<Object> value, Handle<Object> value,
...@@ -8072,8 +8082,7 @@ PropertyCellType PropertyCell::UpdatedType(Isolate* isolate, ...@@ -8072,8 +8082,7 @@ PropertyCellType PropertyCell::UpdatedType(Isolate* isolate,
switch (type) { switch (type) {
// Only allow a cell to transition once into constant state. // Only allow a cell to transition once into constant state.
case PropertyCellType::kUninitialized: case PropertyCellType::kUninitialized:
if (value->IsUndefined(isolate)) return PropertyCellType::kUndefined; return TypeForUninitializedCell(isolate, value);
return PropertyCellType::kConstant;
case PropertyCellType::kInvalidated: case PropertyCellType::kInvalidated:
return PropertyCellType::kMutable; return PropertyCellType::kMutable;
default: default:
...@@ -8122,7 +8131,7 @@ Handle<PropertyCell> PropertyCell::PrepareForValue( ...@@ -8122,7 +8131,7 @@ Handle<PropertyCell> PropertyCell::PrepareForValue(
PropertyCellType new_type = PropertyCellType new_type =
UpdatedType(isolate, cell, value, original_details); UpdatedType(isolate, cell, value, original_details);
if (invalidate) { if (invalidate) {
cell = PropertyCell::InvalidateEntry(isolate, dictionary, entry); cell = PropertyCell::InvalidateAndReplaceEntry(isolate, dictionary, entry);
} }
// Install new property details. // Install new property details.
......
...@@ -31,12 +31,17 @@ class PropertyCell : public HeapObject { ...@@ -31,12 +31,17 @@ class PropertyCell : public HeapObject {
PropertyCellConstantType GetConstantType(); PropertyCellConstantType GetConstantType();
// Computes the new type of a previously uninitialized cell for the given
// value.
static PropertyCellType TypeForUninitializedCell(Isolate* isolate,
Handle<Object> value);
// Computes the new type of the cell's contents for the given value, but // Computes the new type of the cell's contents for the given value, but
// without actually modifying the details. // without actually modifying the details.
static PropertyCellType UpdatedType(Isolate* isolate, static PropertyCellType UpdatedType(Isolate* isolate,
Handle<PropertyCell> cell, Handle<PropertyCell> cell,
Handle<Object> value, Handle<Object> value,
PropertyDetails details); PropertyDetails details);
// Prepares property cell at given entry for receiving given value. // Prepares property cell at given entry for receiving given value.
// As a result the old cell could be invalidated and/or dependent code could // As a result the old cell could be invalidated and/or dependent code could
// be deoptimized. Returns the prepared property cell. // be deoptimized. Returns the prepared property cell.
...@@ -44,7 +49,8 @@ class PropertyCell : public HeapObject { ...@@ -44,7 +49,8 @@ class PropertyCell : public HeapObject {
Isolate* isolate, Handle<GlobalDictionary> dictionary, Isolate* isolate, Handle<GlobalDictionary> dictionary,
InternalIndex entry, Handle<Object> value, PropertyDetails details); InternalIndex entry, Handle<Object> value, PropertyDetails details);
static Handle<PropertyCell> InvalidateEntry( void ClearAndInvalidate(ReadOnlyRoots roots);
static Handle<PropertyCell> InvalidateAndReplaceEntry(
Isolate* isolate, Handle<GlobalDictionary> dictionary, Isolate* isolate, Handle<GlobalDictionary> dictionary,
InternalIndex entry); InternalIndex entry);
......
...@@ -49,6 +49,7 @@ class V8_EXPORT_PRIVATE StringTableShape : public BaseShape<StringTableKey*> { ...@@ -49,6 +49,7 @@ class V8_EXPORT_PRIVATE StringTableShape : public BaseShape<StringTableKey*> {
static const int kPrefixSize = 0; static const int kPrefixSize = 0;
static const int kEntrySize = 1; static const int kEntrySize = 1;
static const bool kMatchNeedsHoleCheck = true;
}; };
class SeqOneByteString; class SeqOneByteString;
...@@ -105,6 +106,7 @@ class StringSetShape : public BaseShape<String> { ...@@ -105,6 +106,7 @@ class StringSetShape : public BaseShape<String> {
static const int kPrefixSize = 0; static const int kPrefixSize = 0;
static const int kEntrySize = 1; static const int kEntrySize = 1;
static const bool kMatchNeedsHoleCheck = true;
}; };
EXTERN_DECLARE_HASH_TABLE(StringSet, StringSetShape) EXTERN_DECLARE_HASH_TABLE(StringSet, StringSetShape)
......
...@@ -79,7 +79,7 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) { ...@@ -79,7 +79,7 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) {
#ifdef DEBUG #ifdef DEBUG
DCHECK(key->IsJSReceiver()); DCHECK(key->IsJSReceiver());
DCHECK(EphemeronHashTable::ShapeT::IsLive(ReadOnlyRoots(isolate), *key)); DCHECK(EphemeronHashTable::IsKey(ReadOnlyRoots(isolate), *key));
Handle<EphemeronHashTable> table( Handle<EphemeronHashTable> table(
EphemeronHashTable::cast(weak_collection->table()), isolate); EphemeronHashTable::cast(weak_collection->table()), isolate);
// Should only be called when shrinking the table is necessary. See // Should only be called when shrinking the table is necessary. See
...@@ -102,7 +102,7 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionSet) { ...@@ -102,7 +102,7 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionSet) {
#ifdef DEBUG #ifdef DEBUG
DCHECK(key->IsJSReceiver()); DCHECK(key->IsJSReceiver());
DCHECK(EphemeronHashTable::ShapeT::IsLive(ReadOnlyRoots(isolate), *key)); DCHECK(EphemeronHashTable::IsKey(ReadOnlyRoots(isolate), *key));
Handle<EphemeronHashTable> table( Handle<EphemeronHashTable> table(
EphemeronHashTable::cast(weak_collection->table()), isolate); EphemeronHashTable::cast(weak_collection->table()), isolate);
// Should only be called when rehashing or resizing the table is necessary. // Should only be called when rehashing or resizing the table is necessary.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment