diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 034e1d5d7071..3124d8d1a63e 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -452,10 +452,6 @@ class HeapPtr : public WriteBarrieredBase DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T); - T unbarrieredGet() const { - return this->value; - } - private: void set(const T& v) { this->pre(); diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 4af88308ec82..96991e8f5d71 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -650,8 +650,7 @@ class GCRuntime uint64_t nextCellUniqueId() { MOZ_ASSERT(nextCellUniqueId_ > 0); - uint64_t uid = ++nextCellUniqueId_; - return uid; + return nextCellUniqueId_++; } public: @@ -1031,7 +1030,7 @@ class GCRuntime size_t maxMallocBytes; // An incrementing id used to assign unique ids to cells that require one. - mozilla::Atomic nextCellUniqueId_; + uint64_t nextCellUniqueId_; /* * Number of the committed arenas in all GC chunks including empty chunks. diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index d4290df22af6..7c3c6f08bd6b 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -431,16 +431,9 @@ struct Zone : public JS::shadow::Zone, uniqueIds_.remove(cell); } - // When finished parsing off-thread, transfer any UIDs we created in the - // off-thread zone into the target zone. - void adoptUniqueIds(JS::Zone* source) { - js::AutoEnterOOMUnsafeRegion oomUnsafe; - for (js::gc::UniqueIdMap::Enum e(source->uniqueIds_); !e.empty(); e.popFront()) { - MOZ_ASSERT(!uniqueIds_.has(e.front().key())); - if (!uniqueIds_.put(e.front().key(), e.front().value())) - oomUnsafe.crash("failed to transfer unique ids from off-main-thread"); - } - source->uniqueIds_.clear(); + // Off-thread parsing should not result in any UIDs being created. + void assertNoUniqueIdsInZone() const { + MOZ_ASSERT(uniqueIds_.count() == 0); } #ifdef JSGC_HASH_TABLE_CHECKS diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 27bc7045d1d9..a8918a6c7450 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -6906,13 +6906,15 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) for (CompartmentsInZoneIter c(source->zone()); !c.done(); c.next()) MOZ_ASSERT(c.get() == source); - // Merge the allocator, stats and UIDs in source's zone into target's zone. + // Merge the allocator in source's zone into target's zone. target->zone()->arenas.adoptArenas(rt, &source->zone()->arenas); target->zone()->usage.adopt(source->zone()->usage); - target->zone()->adoptUniqueIds(source->zone()); // Merge other info in source's zone into target's zone. target->zone()->types.typeLifoAlloc.transferFrom(&source->zone()->types.typeLifoAlloc); + + // Ensure that we did not create any UIDs when running off-thread. + source->zone()->assertNoUniqueIdsInZone(); } void diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 64cae3b8bafd..3a2ec568877e 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -14,7 +14,6 @@ #include "gc/StoreBuffer.h" #include "gc/Zone.h" #include "vm/ArrayObject.h" -#include "vm/TaggedProto.h" #include "vm/UnboxedObject.h" #include "jsobjinlines.h" @@ -378,30 +377,33 @@ struct ObjectGroupCompartment::NewEntry struct Lookup { const Class* clasp; - uint64_t protoUID; - uint64_t assocUID; + TaggedProto hashProto; + TaggedProto matchProto; + JSObject* associated; Lookup(const Class* clasp, TaggedProto proto, JSObject* associated) - : clasp(clasp), - protoUID(proto.uniqueId()), - assocUID(associated ? associated->zone()->getUniqueIdInfallible(associated) : 0) + : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated) + {} + + /* + * For use by generational post barriers only. Look up an entry whose + * proto has been moved, but was hashed with the original value. + */ + Lookup(const Class* clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject* associated) + : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated) {} }; static inline HashNumber hash(const Lookup& lookup) { - HashNumber hash = uintptr_t(lookup.clasp); - hash = mozilla::RotateLeft(hash, 4) ^ Zone::UniqueIdToHash(lookup.protoUID); - hash = mozilla::RotateLeft(hash, 4) ^ Zone::UniqueIdToHash(lookup.assocUID); - return hash; + return PointerHasher::hash(lookup.hashProto.raw()) ^ + PointerHasher::hash(lookup.clasp) ^ + PointerHasher::hash(lookup.associated); } static inline bool match(const NewEntry& key, const Lookup& lookup) { - if (lookup.clasp && key.group.unbarrieredGet()->clasp() != lookup.clasp) - return false; - if (key.group.unbarrieredGet()->proto().unbarrieredGet().uniqueId() != lookup.protoUID) - return false; - return !key.associated || - key.associated->zone()->getUniqueIdInfallible(key.associated) == lookup.assocUID; + return key.group.unbarrieredGet()->proto() == lookup.matchProto && + (!lookup.clasp || key.group.unbarrieredGet()->clasp() == lookup.clasp) && + key.associated == lookup.associated; } static void rekey(NewEntry& k, const NewEntry& newKey) { k = newKey; } @@ -412,6 +414,58 @@ struct ObjectGroupCompartment::NewEntry } }; +// This class is used to add a post barrier on a NewTable entry, as the key is +// calculated from a prototype object which may be moved by generational GC. +class ObjectGroupCompartment::NewTableRef : public gc::BufferableRef +{ + NewTable* table; + const Class* clasp; + JSObject* proto; + JSObject* associated; + + public: + NewTableRef(NewTable* table, const Class* clasp, JSObject* proto, JSObject* associated) + : table(table), clasp(clasp), proto(proto), associated(associated) + {} + + void trace(JSTracer* trc) override { + JSObject* prior = proto; + TraceManuallyBarrieredEdge(trc, &proto, "newObjectGroups set prototype"); + if (prior == proto) + return; + + NewTable::Ptr p = table->lookup(NewTable::Lookup(clasp, TaggedProto(prior), + TaggedProto(proto), + associated)); + if (!p) + return; + + table->rekeyAs(NewTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), + NewTable::Lookup(clasp, TaggedProto(proto), associated), *p); + } +}; + +/* static */ void +ObjectGroupCompartment::newTablePostBarrier(ExclusiveContext* cx, NewTable* table, + const Class* clasp, TaggedProto proto, + JSObject* associated) +{ + MOZ_ASSERT_IF(associated, !IsInsideNursery(associated)); + + if (!proto.isObject()) + return; + + if (!cx->isJSContext()) { + MOZ_ASSERT(!IsInsideNursery(proto.toObject())); + return; + } + + if (IsInsideNursery(proto.toObject())) { + gc::StoreBuffer& sb = cx->asJSContext()->runtime()->gc.storeBuffer; + sb.putGeneric(NewTableRef(table, clasp, proto.toObject(), associated)); + } +} + /* static */ ObjectGroup* ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, TaggedProto proto, JSObject* associated) @@ -505,6 +559,8 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, return nullptr; } + ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, associated); + if (proto.isObject()) { RootedObject obj(cx, proto.toObject()); @@ -583,6 +639,8 @@ ObjectGroup::lazySingletonGroup(ExclusiveContext* cx, const Class* clasp, Tagged return nullptr; } + ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, nullptr); + return group; } @@ -1750,11 +1808,13 @@ ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table) if (table && table->initialized()) { for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { NewEntry entry = e.front(); - ObjectGroup* group = entry.group.unbarrieredGet(); - if (IsForwarded(group)) { - group = Forwarded(group); + + bool needRekey = false; + if (IsForwarded(entry.group.unbarrieredGet())) { + group = Forwarded(entry.group.unbarrieredGet()); entry.group.set(group); + needRekey = true; } TaggedProto proto = group->proto(); if (proto.isObject() && IsForwarded(proto.toObject())) { @@ -1762,9 +1822,19 @@ ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table) // Update the group's proto here so that we are able to lookup // entries in this table before all object pointers are updated. group->proto() = proto; + needRekey = true; } - if (entry.associated && IsForwarded(entry.associated)) + if (entry.associated && IsForwarded(entry.associated)) { entry.associated = Forwarded(entry.associated); + needRekey = true; + } + if (needRekey) { + const Class* clasp = group->clasp(); + if (entry.associated && entry.associated->is()) + clasp = nullptr; + NewEntry::Lookup lookup(clasp, proto, entry.associated); + e.rekeyFront(lookup, entry); + } } } } diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index dca261675a82..5fefd785b2a6 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -540,6 +540,7 @@ class ObjectGroupCompartment struct NewEntry; using NewTable = js::GCHashSet; + class NewTableRef; // Set of default 'new' or lazy groups in the compartment. NewTable* defaultNewTable; @@ -626,6 +627,9 @@ class ObjectGroupCompartment #endif void fixupNewTableAfterMovingGC(NewTable* table); + + static void newTablePostBarrier(ExclusiveContext* cx, NewTable* table, + const Class* clasp, TaggedProto proto, JSObject* associated); }; PlainObject* diff --git a/js/src/vm/TaggedProto.cpp b/js/src/vm/TaggedProto.cpp index dcf5748b4846..725c3bc64e0d 100644 --- a/js/src/vm/TaggedProto.cpp +++ b/js/src/vm/TaggedProto.cpp @@ -10,7 +10,6 @@ #include "jsobj.h" #include "gc/Barrier.h" -#include "gc/Zone.h" namespace js { @@ -31,25 +30,3 @@ InternalBarrierMethods::postBarrier(TaggedProto* vp, TaggedProto pr } } // namespace js - -js::HashNumber -js::TaggedProto::hashCode() const -{ - if (isLazy()) - return HashNumber(1); - JSObject* obj = toObjectOrNull(); - if (!obj) - return HashNumber(0); - return obj->zone()->getHashCodeInfallible(obj); -} - -uint64_t -js::TaggedProto::uniqueId() const -{ - if (isLazy()) - return uint64_t(1); - JSObject* obj = toObjectOrNull(); - if (!obj) - return uint64_t(0); - return obj->zone()->getUniqueIdInfallible(obj); -} diff --git a/js/src/vm/TaggedProto.h b/js/src/vm/TaggedProto.h index b8b928df4a1b..53c5fd575ce9 100644 --- a/js/src/vm/TaggedProto.h +++ b/js/src/vm/TaggedProto.h @@ -44,9 +44,6 @@ class TaggedProto bool operator ==(const TaggedProto& other) const { return proto == other.proto; } bool operator !=(const TaggedProto& other) const { return proto != other.proto; } - HashNumber hashCode() const; - uint64_t uniqueId() const; - void trace(JSTracer* trc) { if (isObject()) TraceManuallyBarrieredEdge(trc, &proto, "TaggedProto"); @@ -86,8 +83,6 @@ class TaggedProtoOperations inline JSObject* toObject() const { return value().toObject(); } inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); } JSObject* raw() const { return value().raw(); } - HashNumber hashCode() const { return value().hashCode(); } - uint64_t uniqueId() const { return value().uniqueId(); } }; template <>