From 270b69bc5ce2049d84f661be428a20c769ab99e7 Mon Sep 17 00:00:00 2001 From: "Gang Zhao (Hermes)" Date: Mon, 9 Dec 2024 22:12:53 -0800 Subject: [PATCH] Pass the pointer of owning object to GCPointer constructor and set method (#1502) Summary: Pull Request resolved: https://github.com/facebook/hermes/pull/1502 Differential Revision: D62222257 --- include/hermes/VM/GCPointer-inline.h | 56 ++++++++++++++++++ include/hermes/VM/GCPointer.h | 85 ++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 4 deletions(-) diff --git a/include/hermes/VM/GCPointer-inline.h b/include/hermes/VM/GCPointer-inline.h index fa5f7633ed5..5778f97bcb2 100644 --- a/include/hermes/VM/GCPointer-inline.h +++ b/include/hermes/VM/GCPointer-inline.h @@ -32,6 +32,24 @@ GCPointerBase::GCPointerBase( } } +template +GCPointerBase::GCPointerBase( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj, + NeedsBarriers) + : CompressedPointer(CompressedPointer::encode(ptr, base)) { + assert( + (!ptr || gc.validPointer(ptr)) && + "Cannot construct a GCPointer from an invalid pointer"); + if constexpr (NeedsBarriers::value) { + gc.constructorWriteBarrierForLargeObj(owningObj, this, ptr); + } else { + assert(!gc.needsWriteBarrier(this, ptr)); + } +} + inline void GCPointerBase::set(PointerBase &base, GCCell *ptr, GC &gc) { assert( (!ptr || gc.validPointer(ptr)) && @@ -59,6 +77,44 @@ GCPointerBase::set(PointerBase &base, CompressedPointer ptr, GC &gc) { setNoBarrier(ptr); } +inline void GCPointerBase::setInLargeObj( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj) { + assert( + (!ptr || gc.validPointer(ptr)) && + "Cannot set a GCPointer to an invalid pointer"); + // Write barrier must happen before the write. + gc.writeBarrierForLargeObj(owningObj, this, ptr); + setNoBarrier(CompressedPointer::encode(ptr, base)); +} + +inline void GCPointerBase::setNonNullInLargeObj( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj) { + assert( + gc.validPointer(ptr) && "Cannot set a GCPointer to an invalid pointer"); + // Write barrier must happen before the write. + gc.writeBarrierForLargeObj(owningObj, this, ptr); + setNoBarrier(CompressedPointer::encodeNonNull(ptr, base)); +} + +inline void GCPointerBase::setInLargeObj( + PointerBase &base, + CompressedPointer ptr, + GC &gc, + const GCCell *owningObj) { + assert( + (!ptr || gc.validPointer(ptr.get(base))) && + "Cannot set a GCPointer to an invalid pointer"); + // Write barrier must happen before the write. + gc.writeBarrierForLargeObj(owningObj, this, ptr.get(base)); + setNoBarrier(ptr); +} + inline void GCPointerBase::setNull(GC &gc) { gc.snapshotWriteBarrier(this); setNoBarrier(CompressedPointer(nullptr)); diff --git a/include/hermes/VM/GCPointer.h b/include/hermes/VM/GCPointer.h index 09db5f06d87..05d7fa7e3bb 100644 --- a/include/hermes/VM/GCPointer.h +++ b/include/hermes/VM/GCPointer.h @@ -27,6 +27,14 @@ class GCPointerBase : public CompressedPointer { template inline GCPointerBase(PointerBase &base, GCCell *ptr, GC &gc, NeedsBarriers); + template + inline GCPointerBase( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj, + NeedsBarriers); + public: // These classes are used as arguments to GCPointer constructors, to // indicate whether write barriers are necessary in initializing the @@ -34,7 +42,8 @@ class GCPointerBase : public CompressedPointer { class NoBarriers : public std::false_type {}; class YesBarriers : public std::true_type {}; - /// This must be used to assign a new value to this GCPointer. + /// This must be used to assign a new value to this GCPointer. This must not + /// be used if it lives in an object that supports large allocation. /// \param ptr The memory being pointed to. /// \param base The base of ptr. /// \param gc Used for write barriers. @@ -42,6 +51,29 @@ class GCPointerBase : public CompressedPointer { inline void set(PointerBase &base, CompressedPointer ptr, GC &gc); inline void setNonNull(PointerBase &base, GCCell *ptr, GC &gc); + /// This must be used to assign a new value to this GCPointer, which lives in + /// an object of kind that supports large allocation. + /// \param ptr The memory being pointed to. + /// \param base The base of ptr. + /// \param gc Used for write barriers. + /// \param owningObj The object that contains this GCPointer, used by the + /// writer barriers. + inline void setInLargeObj( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj); + inline void setInLargeObj( + PointerBase &base, + CompressedPointer ptr, + GC &gc, + const GCCell *owningObj); + inline void setNonNullInLargeObj( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj); + /// Set this pointer to null. This needs a write barrier in some types of /// garbage collectors. inline void setNull(GC &gc); @@ -64,12 +96,26 @@ class GCPointer : public GCPointerBase { template GCPointer(PointerBase &base, T *ptr, GC &gc, NeedsBarriers needsBarriers) : GCPointerBase(base, ptr, gc, needsBarriers) {} + /// Pass the owning object pointer to perform barriers when the object + /// supports large allocation. + template + GCPointer( + PointerBase &base, + T *ptr, + GC &gc, + const GCCell *owningObj, + NeedsBarriers needsBarriers) + : GCPointerBase(base, ptr, gc, owningObj, needsBarriers) {} /// Same as the constructor above, with the default for /// NeedsBarriers as "YesBarriers". (We can't use default template /// arguments with the idiom used above.) - inline GCPointer(PointerBase &base, T *ptr, GC &gc) + GCPointer(PointerBase &base, T *ptr, GC &gc) : GCPointer(base, ptr, gc, YesBarriers()) {} + /// Pass the owning object pointer to perform barriers when the object + /// supports large allocation. + GCPointer(PointerBase &base, T *ptr, GC &gc, const GCCell *owningObj) + : GCPointer(base, ptr, gc, owningObj, YesBarriers()) {} /// We are not allowed to copy-construct or assign GCPointers. GCPointer(const GCPointerBase &) = delete; @@ -86,7 +132,8 @@ class GCPointer : public GCPointerBase { return vmcast(GCPointerBase::getNonNull(base)); } - /// Assign a new value to this GCPointer. + /// Assign a new value to this GCPointer. This must not be used if it lives in + /// an object that supports large allocation. /// \param base The base of ptr. /// \param ptr The memory being pointed to. /// \param gc Used for write barriers. @@ -97,10 +144,40 @@ class GCPointer : public GCPointerBase { GCPointerBase::setNonNull(base, ptr, gc); } - /// Convenience overload of GCPointer::set for other GCPointers. + /// Assign a new value to this GCPointer, which lives in an object of kind + /// that supports large allocation. + /// \param base The base of ptr. + /// \param ptr The memory being pointed to. + /// \param gc Used for write barriers. + /// \param owningObj The object that contains this GCPointer, used by the + /// writer barriers. + void + setInLargeObj(PointerBase &base, T *ptr, GC &gc, const GCCell *owningObj) { + GCPointerBase::set(base, ptr, gc, owningObj); + } + void setNonNullInLargeObj( + PointerBase &base, + T *ptr, + GC &gc, + const GCCell *owningObj) { + GCPointerBase::setNonNull(base, ptr, gc, owningObj); + } + + /// Convenience overload of GCPointer::set for other GCPointers. This must not + /// be used if it lives in an object that supports large allocation. void set(PointerBase &base, const GCPointer &ptr, GC &gc) { GCPointerBase::set(base, ptr, gc); } + + /// Convenience overload of GCPointer::set for other GCPointers. \p owningObj + /// is used by the writer barriers. + void setInLargeObj( + PointerBase &base, + const GCPointer &ptr, + GC &gc, + const GCCell *owningObj) { + GCPointerBase::set(base, ptr, gc, owningObj); + } }; } // namespace vm