diff --git a/js/public/Id.h b/js/public/Id.h index 844b01f92250..ae4cfb561307 100644 --- a/js/public/Id.h +++ b/js/public/Id.h @@ -92,8 +92,13 @@ struct PropertyKey { return reinterpret_cast(asBits ^ JSID_TYPE_SYMBOL); } + js::gc::Cell* toGCThing() const { + MOZ_ASSERT(isGCThing()); + return reinterpret_cast(asBits & ~(size_t)JSID_TYPE_MASK); + } + GCCellPtr toGCCellPtr() const { - void* thing = (void*)(asBits & ~(size_t)JSID_TYPE_MASK); + js::gc::Cell* thing = toGCThing(); if (isString()) { return JS::GCCellPtr(thing, JS::TraceKind::String); } diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index a35f1763d4d7..284ff3751f3d 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -103,20 +103,103 @@ AutoTouchingGrayThings::~AutoTouchingGrayThings() { #endif // DEBUG +// Tagged pointer barriers +// +// It's tempting to use ApplyGCThingTyped to dispatch to the typed barrier +// functions (e.g. gc::ReadBarrier(JSObject*)) but this does not compile well +// (clang generates 1580 bytes on x64 versus 296 bytes for this implementation +// of ValueReadBarrier). +// +// Instead, check known special cases and call the generic barrier functions. + +static MOZ_ALWAYS_INLINE bool ValueIsPermanent(const Value& value) { + gc::Cell* cell = value.toGCThing(); + + if (value.isString()) { + return cell->as()->isPermanentAndMayBeShared(); + } + + if (value.isSymbol()) { + return cell->as()->isPermanentAndMayBeShared(); + } + +#ifdef DEBUG + // Using mozilla::DebugOnly here still generated code in opt builds. + bool isPermanent = MapGCThingTyped(value, [](auto t) { + return t->isPermanentAndMayBeShared(); + }).value(); + MOZ_ASSERT(!isPermanent); +#endif + + return false; +} + void gc::ValueReadBarrier(const Value& v) { - ApplyGCThingTyped(v, [](auto t) { gc::ReadBarrier(t); }); + MOZ_ASSERT(v.isGCThing()); + + if (!ValueIsPermanent(v)) { + ReadBarrierImpl(v.toGCThing()); + } } void gc::ValuePreWriteBarrier(const Value& v) { - ApplyGCThingTyped(v, [](auto t) { gc::PreWriteBarrier(t); }); + MOZ_ASSERT(v.isGCThing()); + + if (!ValueIsPermanent(v)) { + PreWriteBarrierImpl(v.toGCThing()); + } +} + +static MOZ_ALWAYS_INLINE bool IdIsPermanent(jsid id) { + gc::Cell* cell = id.toGCThing(); + + if (id.isString()) { + return cell->as()->isPermanentAndMayBeShared(); + } + + if (id.isSymbol()) { + return cell->as()->isPermanentAndMayBeShared(); + } + +#ifdef DEBUG + bool isPermanent = MapGCThingTyped(id, [](auto t) { + return t->isPermanentAndMayBeShared(); + }).value(); + MOZ_ASSERT(!isPermanent); +#endif + + return false; } void gc::IdPreWriteBarrier(jsid id) { - ApplyGCThingTyped(id, [](auto t) { gc::PreWriteBarrier(t); }); + MOZ_ASSERT(id.isGCThing()); + + if (!IdIsPermanent(id)) { + PreWriteBarrierImpl(&id.toGCThing()->asTenured()); + } +} + +static MOZ_ALWAYS_INLINE bool CellPtrIsPermanent(JS::GCCellPtr thing) { + if (thing.mayBeOwnedByOtherRuntime()) { + return true; + } + +#ifdef DEBUG + bool isPermanent = MapGCThingTyped(thing, [](auto t) { + return t->isPermanentAndMayBeShared(); + }); + MOZ_ASSERT(!isPermanent); +#endif + + return false; } void gc::CellPtrPreWriteBarrier(JS::GCCellPtr thing) { - ApplyGCThingTyped(thing, [](auto t) { gc::PreWriteBarrier(t); }); + MOZ_ASSERT(thing); + + if (!CellPtrIsPermanent(thing)) { + PreWriteBarrierImpl(thing.asCell()); + } } template