From 921d9d618f6a12d0359d7cfcf39b90f6e7ea4e6f Mon Sep 17 00:00:00 2001 From: "Gang Zhao (Hermes)" Date: Sat, 21 Dec 2024 00:06:47 -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 | 53 ++++++++++++++++++ include/hermes/VM/GCPointer.h | 83 ++++++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/include/hermes/VM/GCPointer-inline.h b/include/hermes/VM/GCPointer-inline.h index fa5f7633ed5..0122cf26cc6 100644 --- a/include/hermes/VM/GCPointer-inline.h +++ b/include/hermes/VM/GCPointer-inline.h @@ -32,6 +32,21 @@ GCPointerBase::GCPointerBase( } } +GCPointerBase::GCPointerBase( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj) + : CompressedPointer(CompressedPointer::encode(ptr, base)) { + assert( + (!ptr || gc.validPointer(ptr)) && + "Cannot construct a GCPointer from an invalid pointer"); + // Constructing a GCPointer on an object that supports large allocation always + // needs to perform write barrier. We may revisit this decision if we see a + // case that needs optimization. + gc.constructorWriteBarrierForLargeObj(owningObj, this, ptr); +} + inline void GCPointerBase::set(PointerBase &base, GCCell *ptr, GC &gc) { assert( (!ptr || gc.validPointer(ptr)) && @@ -59,6 +74,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..db1887e096d 100644 --- a/include/hermes/VM/GCPointer.h +++ b/include/hermes/VM/GCPointer.h @@ -27,6 +27,12 @@ class GCPointerBase : public CompressedPointer { template inline GCPointerBase(PointerBase &base, GCCell *ptr, GC &gc, NeedsBarriers); + inline GCPointerBase( + PointerBase &base, + GCCell *ptr, + GC &gc, + const GCCell *owningObj); + public: // These classes are used as arguments to GCPointer constructors, to // indicate whether write barriers are necessary in initializing the @@ -34,7 +40,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 +49,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 +94,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 +130,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 +142,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