Commit 76b9d98f authored by Jakob Gruber's avatar Jakob Gruber Committed by V8 LUCI CQ

[compiler] Concurrent JSGlobalObjectRef::GetPropertyCell

.. and make JSGlobalObjectRef bg-serialized.

GetPropertyCell was implemented as:

 LookupIterator it(holder, isolate, name, LookupIterator::OWN);
 it.TryLookupCachedProperty();
 if (it.state() == LookupIterator::DATA) it.GetPropertyCell();

Due to concurrency requirements, we essentially have to reimplement
this entire path for use in a concurrent setting:

 - Reads in some cases have to use relaxed or acquire semantics.
 - The IsPendingAllocation predicate must be called on some objects
   before reading into them.
 - Repeated reads of the same field must be avoided due to the
   possibility of concurrent modifications.

This CL introduces two new methods:

ConcurrentLookupIterator::TryGetPropertyCell implements the outer
lookup logic, including the repeated lookup for accessors / cached
property names.

GlobalDictionary::TryFindPropertyCellForConcurrentLookupIterator is a
slightly modified HashTable::FindEntry which follows the above rules.

Bug: v8:7790
Change-Id: Ic9a52da766afdfedce8efcbda92876845a17eed9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2959616Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75467}
parent 8f84d0bb
...@@ -1321,11 +1321,6 @@ void AllocationSiteData::SerializeBoilerplate(JSHeapBroker* broker) { ...@@ -1321,11 +1321,6 @@ void AllocationSiteData::SerializeBoilerplate(JSHeapBroker* broker) {
HeapObjectData::HeapObjectData(JSHeapBroker* broker, ObjectData** storage, HeapObjectData::HeapObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<HeapObject> object, ObjectDataKind kind) Handle<HeapObject> object, ObjectDataKind kind)
: ObjectData(broker, storage, object, kind), : ObjectData(broker, storage, object, kind),
// We have to use a raw cast below instead of AsMap() because of
// recursion. AsMap() would call IsMap(), which accesses the
// instance_type_ member. In the case of constructing the MapData for the
// meta map (whose map is itself), this member has not yet been
// initialized.
map_(broker->GetOrCreateData(object->map(kAcquireLoad), map_(broker->GetOrCreateData(object->map(kAcquireLoad),
kAssumeMemoryFence)) { kAssumeMemoryFence)) {
CHECK_IMPLIES(kind == kSerializedHeapObject, CHECK_IMPLIES(kind == kSerializedHeapObject,
...@@ -2019,15 +2014,28 @@ class CellData : public HeapObjectData { ...@@ -2019,15 +2014,28 @@ class CellData : public HeapObjectData {
class JSGlobalObjectData : public JSObjectData { class JSGlobalObjectData : public JSObjectData {
public: public:
JSGlobalObjectData(JSHeapBroker* broker, ObjectData** storage, JSGlobalObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalObject> object); Handle<JSGlobalObject> object,
bool IsDetached() const { return is_detached_; } ObjectDataKind kind = kSerializedHeapObject)
: JSObjectData(broker, storage, object, kind),
properties_(broker->zone()) {
if (!broker->is_concurrent_inlining()) {
is_detached_ = object->IsDetached();
}
}
bool IsDetached() const {
DCHECK_EQ(kind(), kSerializedHeapObject);
return is_detached_;
}
ObjectData* GetPropertyCell( ObjectData* GetPropertyCell(
JSHeapBroker* broker, ObjectData* name, JSHeapBroker* broker, ObjectData* name,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
private: private:
bool const is_detached_; // Only valid if not concurrent inlining.
bool is_detached_ = false;
// Properties that either // Properties that either
// (1) are known to exist as property cells on the global object, or // (1) are known to exist as property cells on the global object, or
...@@ -2036,13 +2044,6 @@ class JSGlobalObjectData : public JSObjectData { ...@@ -2036,13 +2044,6 @@ class JSGlobalObjectData : public JSObjectData {
ZoneVector<std::pair<ObjectData*, ObjectData*>> properties_; ZoneVector<std::pair<ObjectData*, ObjectData*>> properties_;
}; };
JSGlobalObjectData::JSGlobalObjectData(JSHeapBroker* broker,
ObjectData** storage,
Handle<JSGlobalObject> object)
: JSObjectData(broker, storage, object),
is_detached_(object->IsDetached()),
properties_(broker->zone()) {}
class JSGlobalProxyData : public JSObjectData { class JSGlobalProxyData : public JSObjectData {
public: public:
JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage, JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
...@@ -2055,17 +2056,12 @@ namespace { ...@@ -2055,17 +2056,12 @@ namespace {
base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker, base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker,
Handle<Name> name) { Handle<Name> name) {
LookupIterator it( base::Optional<PropertyCell> maybe_cell =
broker->isolate(), ConcurrentLookupIterator::TryGetPropertyCell(
handle(broker->target_native_context().object()->global_object(), broker->isolate(), broker->local_isolate_or_isolate(),
broker->isolate()), broker->target_native_context().global_object().object(), name);
name, LookupIterator::OWN); if (!maybe_cell.has_value()) return {};
it.TryLookupCachedProperty(); return TryMakeRef(broker, *maybe_cell);
if (it.state() == LookupIterator::DATA &&
it.GetHolder<JSObject>()->IsJSGlobalObject()) {
return TryMakeRef(broker, it.GetPropertyCell());
}
return base::nullopt;
} }
} // namespace } // namespace
...@@ -2073,6 +2069,8 @@ base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker, ...@@ -2073,6 +2069,8 @@ base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker,
ObjectData* JSGlobalObjectData::GetPropertyCell(JSHeapBroker* broker, ObjectData* JSGlobalObjectData::GetPropertyCell(JSHeapBroker* broker,
ObjectData* name, ObjectData* name,
SerializationPolicy policy) { SerializationPolicy policy) {
DCHECK_EQ(kind(), kSerializedHeapObject);
CHECK_NOT_NULL(name); CHECK_NOT_NULL(name);
for (auto const& p : properties_) { for (auto const& p : properties_) {
if (p.first == name) return p.second; if (p.first == name) return p.second;
...@@ -4353,9 +4351,10 @@ bool NativeContextRef::GlobalIsDetached() const { ...@@ -4353,9 +4351,10 @@ bool NativeContextRef::GlobalIsDetached() const {
base::Optional<PropertyCellRef> JSGlobalObjectRef::GetPropertyCell( base::Optional<PropertyCellRef> JSGlobalObjectRef::GetPropertyCell(
NameRef const& name, SerializationPolicy policy) const { NameRef const& name, SerializationPolicy policy) const {
if (data_->should_access_heap()) { if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
return GetPropertyCellFromHeap(broker(), name.object()); return GetPropertyCellFromHeap(broker(), name.object());
} }
ObjectData* property_cell_data = data()->AsJSGlobalObject()->GetPropertyCell( ObjectData* property_cell_data = data()->AsJSGlobalObject()->GetPropertyCell(
broker(), name.data(), policy); broker(), name.data(), policy);
return TryMakeRef<PropertyCell>(broker(), property_cell_data); return TryMakeRef<PropertyCell>(broker(), property_cell_data);
......
...@@ -84,7 +84,7 @@ enum class RefSerializationKind { ...@@ -84,7 +84,7 @@ enum class RefSerializationKind {
V(JSBoundFunction, RefSerializationKind::kBackgroundSerialized) \ V(JSBoundFunction, RefSerializationKind::kBackgroundSerialized) \
V(JSDataView, RefSerializationKind::kBackgroundSerialized) \ V(JSDataView, RefSerializationKind::kBackgroundSerialized) \
V(JSFunction, RefSerializationKind::kSerialized) \ V(JSFunction, RefSerializationKind::kSerialized) \
V(JSGlobalObject, RefSerializationKind::kSerialized) \ V(JSGlobalObject, RefSerializationKind::kBackgroundSerialized) \
V(JSGlobalProxy, RefSerializationKind::kBackgroundSerialized) \ V(JSGlobalProxy, RefSerializationKind::kBackgroundSerialized) \
V(JSTypedArray, RefSerializationKind::kBackgroundSerialized) \ V(JSTypedArray, RefSerializationKind::kBackgroundSerialized) \
/* Subtypes of Context */ \ /* Subtypes of Context */ \
...@@ -996,13 +996,7 @@ class JSGlobalObjectRef : public JSObjectRef { ...@@ -996,13 +996,7 @@ class JSGlobalObjectRef : public JSObjectRef {
bool IsDetachedFrom(JSGlobalProxyRef const& proxy) const; bool IsDetachedFrom(JSGlobalProxyRef const& proxy) const;
// If {serialize} is false: // Can be called even when there is no property cell for the given name.
// If the property is known to exist as a property cell (on the global
// object), return that property cell. Otherwise (not known to exist as a
// property cell or known not to exist as a property cell) return nothing.
// If {serialize} is true:
// Like above but potentially access the heap and serialize the necessary
// information.
base::Optional<PropertyCellRef> GetPropertyCell( base::Optional<PropertyCellRef> GetPropertyCell(
NameRef const& name, SerializationPolicy policy = NameRef const& name, SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const; SerializationPolicy::kAssumeSerialized) const;
......
...@@ -1212,6 +1212,9 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl( ...@@ -1212,6 +1212,9 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl(
if (compilation_info()->is_osr()) data_.InitializeOsrHelper(); if (compilation_info()->is_osr()) data_.InitializeOsrHelper();
// Serialize() and CreateGraph() may already use IsPendingAllocation.
isolate->heap()->PublishPendingAllocations();
pipeline_.Serialize(); pipeline_.Serialize();
if (!data_.broker()->is_concurrent_inlining()) { if (!data_.broker()->is_concurrent_inlining()) {
...@@ -1222,6 +1225,7 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl( ...@@ -1222,6 +1225,7 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl(
} }
if (compilation_info()->concurrent_inlining()) { if (compilation_info()->concurrent_inlining()) {
// Serialization may have allocated.
isolate->heap()->PublishPendingAllocations(); isolate->heap()->PublishPendingAllocations();
} }
......
...@@ -660,6 +660,10 @@ bool Heap::IsPendingAllocation(HeapObject object) { ...@@ -660,6 +660,10 @@ bool Heap::IsPendingAllocation(HeapObject object) {
UNREACHABLE(); UNREACHABLE();
} }
bool Heap::IsPendingAllocation(Object object) {
return object.IsHeapObject() && IsPendingAllocation(HeapObject::cast(object));
}
void Heap::ExternalStringTable::AddString(String string) { void Heap::ExternalStringTable::AddString(String string) {
DCHECK(string.IsExternalString()); DCHECK(string.IsExternalString());
DCHECK(!Contains(string)); DCHECK(!Contains(string));
......
...@@ -1548,6 +1548,7 @@ class Heap { ...@@ -1548,6 +1548,7 @@ class Heap {
// as uninitialized to background threads. // as uninitialized to background threads.
// This predicate may be invoked from a background thread. // This predicate may be invoked from a background thread.
inline bool IsPendingAllocation(HeapObject object); inline bool IsPendingAllocation(HeapObject object);
inline bool IsPendingAllocation(Object object);
// Notifies that all previously allocated objects are properly initialized // Notifies that all previously allocated objects are properly initialized
// and ensures that IsPendingAllocation returns false for them. This function // and ensures that IsPendingAllocation returns false for them. This function
......
...@@ -243,6 +243,9 @@ class V8_EXPORT_PRIVATE GlobalDictionary ...@@ -243,6 +243,9 @@ class V8_EXPORT_PRIVATE GlobalDictionary
inline Name NameAt(PtrComprCageBase cage_base, InternalIndex entry); inline Name NameAt(PtrComprCageBase cage_base, InternalIndex entry);
inline void ValueAtPut(InternalIndex entry, Object value); inline void ValueAtPut(InternalIndex entry, Object value);
base::Optional<PropertyCell> TryFindPropertyCellForConcurrentLookupIterator(
Isolate* isolate, Handle<Name> name, RelaxedLoadTag tag);
OBJECT_CONSTRUCTORS( OBJECT_CONSTRUCTORS(
GlobalDictionary, GlobalDictionary,
BaseNameDictionary<GlobalDictionary, GlobalDictionaryShape>); BaseNameDictionary<GlobalDictionary, GlobalDictionaryShape>);
......
...@@ -146,7 +146,6 @@ InternalIndex HashTable<Derived, Shape>::FindEntry(PtrComprCageBase cage_base, ...@@ -146,7 +146,6 @@ InternalIndex HashTable<Derived, Shape>::FindEntry(PtrComprCageBase cage_base,
uint32_t count = 1; uint32_t count = 1;
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);
// EnsureCapacity will guarantee the hash table is never full. // EnsureCapacity will guarantee the hash table is never full.
for (InternalIndex entry = FirstProbe(hash, capacity);; for (InternalIndex entry = FirstProbe(hash, capacity);;
entry = NextProbe(entry, count++, capacity)) { entry = NextProbe(entry, count++, capacity)) {
...@@ -196,6 +195,20 @@ Object HashTable<Derived, Shape>::KeyAt(PtrComprCageBase cage_base, ...@@ -196,6 +195,20 @@ Object HashTable<Derived, Shape>::KeyAt(PtrComprCageBase cage_base,
return get(cage_base, EntryToIndex(entry) + kEntryKeyIndex); return get(cage_base, EntryToIndex(entry) + kEntryKeyIndex);
} }
template <typename Derived, typename Shape>
Object HashTable<Derived, Shape>::KeyAt(InternalIndex entry,
RelaxedLoadTag tag) {
PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
return KeyAt(cage_base, entry, tag);
}
template <typename Derived, typename Shape>
Object HashTable<Derived, Shape>::KeyAt(PtrComprCageBase cage_base,
InternalIndex entry,
RelaxedLoadTag tag) {
return get(cage_base, EntryToIndex(entry) + kEntryKeyIndex, tag);
}
template <typename Derived, typename Shape> template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::set_key(int index, Object value) { void HashTable<Derived, Shape>::set_key(int index, Object value) {
DCHECK(!IsEphemeronHashTable()); DCHECK(!IsEphemeronHashTable());
......
...@@ -157,6 +157,9 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable ...@@ -157,6 +157,9 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable
// Returns the key at entry. // Returns the key at entry.
inline Object KeyAt(InternalIndex entry); inline Object KeyAt(InternalIndex entry);
inline Object KeyAt(PtrComprCageBase cage_base, InternalIndex entry); inline Object KeyAt(PtrComprCageBase cage_base, InternalIndex entry);
inline Object KeyAt(InternalIndex entry, RelaxedLoadTag tag);
inline Object KeyAt(PtrComprCageBase cage_base, InternalIndex entry,
RelaxedLoadTag tag);
static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize; static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize;
static const int kEntrySize = Shape::kEntrySize; static const int kEntrySize = Shape::kEntrySize;
......
...@@ -1511,5 +1511,43 @@ ConcurrentLookupIterator::TryGetOwnConstantElement( ...@@ -1511,5 +1511,43 @@ ConcurrentLookupIterator::TryGetOwnConstantElement(
UNREACHABLE(); UNREACHABLE();
} }
// static
base::Optional<PropertyCell> ConcurrentLookupIterator::TryGetPropertyCell(
Isolate* isolate, LocalIsolate* local_isolate,
Handle<JSGlobalObject> holder, Handle<Name> name) {
DisallowGarbageCollection no_gc;
Map holder_map = holder->map();
CHECK(!holder_map.is_access_check_needed());
CHECK(!holder_map.has_named_interceptor());
GlobalDictionary dict = holder->global_dictionary(kAcquireLoad);
base::Optional<PropertyCell> cell =
dict.TryFindPropertyCellForConcurrentLookupIterator(isolate, name,
kRelaxedLoad);
if (!cell.has_value()) return {};
if (cell->property_details().kind() == kAccessor) {
Object maybe_accessor_pair = cell->value(kAcquireLoad);
if (!maybe_accessor_pair.IsAccessorPair()) return {};
base::Optional<Name> maybe_cached_property_name =
FunctionTemplateInfo::TryGetCachedPropertyName(
isolate, AccessorPair::cast(maybe_accessor_pair)
.getter(isolate, kRelaxedLoad));
if (!maybe_cached_property_name.has_value()) return {};
cell = dict.TryFindPropertyCellForConcurrentLookupIterator(
isolate, handle(*maybe_cached_property_name, local_isolate),
kRelaxedLoad);
if (!cell.has_value()) return {};
if (cell->property_details().kind() != kData) return {};
}
DCHECK(cell.has_value());
DCHECK_EQ(cell->property_details().kind(), kData);
return cell;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -344,6 +344,15 @@ class ConcurrentLookupIterator final : public AllStatic { ...@@ -344,6 +344,15 @@ class ConcurrentLookupIterator final : public AllStatic {
Object* result_out, Isolate* isolate, LocalIsolate* local_isolate, Object* result_out, Isolate* isolate, LocalIsolate* local_isolate,
JSObject holder, FixedArrayBase elements, ElementsKind elements_kind, JSObject holder, FixedArrayBase elements, ElementsKind elements_kind,
size_t index); size_t index);
// This method reimplements the following sequence in a concurrent setting:
//
// LookupIterator it(holder, isolate, name, LookupIterator::OWN);
// it.TryLookupCachedProperty();
// if (it.state() == LookupIterator::DATA) it.GetPropertyCell();
V8_EXPORT_PRIVATE static base::Optional<PropertyCell> TryGetPropertyCell(
Isolate* isolate, LocalIsolate* local_isolate,
Handle<JSGlobalObject> holder, Handle<Name> name);
}; };
} // namespace internal } // namespace internal
......
...@@ -5864,6 +5864,37 @@ InternalIndex HashTable<Derived, Shape>::FindInsertionEntry(Isolate* isolate, ...@@ -5864,6 +5864,37 @@ InternalIndex HashTable<Derived, Shape>::FindInsertionEntry(Isolate* isolate,
return FindInsertionEntry(isolate, ReadOnlyRoots(isolate), hash); return FindInsertionEntry(isolate, ReadOnlyRoots(isolate), hash);
} }
base::Optional<PropertyCell>
GlobalDictionary::TryFindPropertyCellForConcurrentLookupIterator(
Isolate* isolate, Handle<Name> name, RelaxedLoadTag tag) {
// This reimplements HashTable::FindEntry for use in a concurrent setting.
// 1) Atomic loads.
// 2) IsPendingAllocation checks.
// 3) Return the PropertyCell value instead of the InternalIndex to avoid a
// repeated load (unsafe with concurrent modifications).
DisallowGarbageCollection no_gc;
PtrComprCageBase cage_base{isolate};
ReadOnlyRoots roots(isolate);
const int32_t hash = ShapeT::Hash(roots, name);
const uint32_t capacity = Capacity();
uint32_t count = 1;
Object undefined = roots.undefined_value();
Object the_hole = roots.the_hole_value();
// EnsureCapacity will guarantee the hash table is never full.
for (InternalIndex entry = FirstProbe(hash, capacity);;
entry = NextProbe(entry, count++, capacity)) {
Object element = KeyAt(cage_base, entry, kRelaxedLoad);
if (isolate->heap()->IsPendingAllocation(element)) return {};
if (element == undefined) return {};
if (ShapeT::kMatchNeedsHoleCheck && element == the_hole) continue;
if (!ShapeT::IsMatch(name, element)) continue;
CHECK(element.IsPropertyCell(cage_base));
return PropertyCell::cast(element);
}
return {};
}
Handle<StringSet> StringSet::New(Isolate* isolate) { Handle<StringSet> StringSet::New(Isolate* isolate) {
return HashTable::New(isolate, 0); return HashTable::New(isolate, 0);
} }
......
...@@ -40,6 +40,14 @@ void AccessorPair::set(AccessorComponent component, Object value) { ...@@ -40,6 +40,14 @@ void AccessorPair::set(AccessorComponent component, Object value) {
} }
} }
DEF_GETTER(AccessorPair, getter, Object) {
return TorqueGeneratedClass::getter(cage_base);
}
DEF_RELAXED_GETTER(AccessorPair, getter, Object) {
return TaggedField<Object, kGetterOffset>::Relaxed_Load(cage_base, *this);
}
void AccessorPair::SetComponents(Object getter, Object setter) { void AccessorPair::SetComponents(Object getter, Object setter) {
if (!getter.IsNull()) set_getter(getter); if (!getter.IsNull()) set_getter(getter);
if (!setter.IsNull()) set_setter(setter); if (!setter.IsNull()) set_setter(setter);
......
...@@ -48,6 +48,9 @@ class AccessorPair : public TorqueGeneratedAccessorPair<AccessorPair, Struct> { ...@@ -48,6 +48,9 @@ class AccessorPair : public TorqueGeneratedAccessorPair<AccessorPair, Struct> {
inline Object get(AccessorComponent component); inline Object get(AccessorComponent component);
inline void set(AccessorComponent component, Object value); inline void set(AccessorComponent component, Object value);
DECL_GETTER(getter, Object)
DECL_RELAXED_GETTER(getter, Object)
// Note: Returns undefined if the component is not set. // Note: Returns undefined if the component is not set.
static Handle<Object> GetComponent(Isolate* isolate, static Handle<Object> GetComponent(Isolate* isolate,
Handle<NativeContext> native_context, Handle<NativeContext> native_context,
......
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