blob: bc29f74193398d14cff4253f62686df18247830a [file] [log] [blame]
/*
* Copyright (C) 2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "JSCJSValue.h"
#include "MarkStack.h"
#include "RootMarkReason.h"
#include "VisitRaceKey.h"
#include <wtf/ConcurrentPtrHashSet.h>
#include <wtf/SharedTask.h>
#include <wtf/text/CString.h>
namespace JSC {
class ConservativeRoots;
class Heap;
class HeapCell;
class JSCell;
class MarkedBlock;
class MarkingConstraint;
class MarkingConstraintSolver;
class PreciseAllocation;
class SlotVisitor;
class VM;
class VerifierSlotVisitor;
template<typename T> class Weak;
template<typename T, typename Traits> class WriteBarrierBase;
class AbstractSlotVisitor {
WTF_MAKE_NONCOPYABLE(AbstractSlotVisitor);
WTF_MAKE_FAST_ALLOCATED;
public:
enum OpaqueRootTag { OpaqueRoot };
class ReferrerToken {
public:
ReferrerToken() = default;
ReferrerToken(std::nullptr_t) { }
ReferrerToken(HeapCell*);
ReferrerToken(OpaqueRootTag, void* opaqueRoot);
ReferrerToken(RootMarkReason);
explicit operator bool() const { return m_bits; }
bool operator!() const { return !m_bits; }
HeapCell* asCell() const;
void* asOpaqueRoot() const;
RootMarkReason asRootMarkReason() const;
private:
enum {
HeapCellToken = 0,
OpaqueRootToken = 0b01,
RootMarkReasonToken = 0b10,
};
static constexpr uintptr_t tokenTypeShift = 2;
static constexpr uintptr_t tokenTypeMask = 0x3;
bool isHeapCell() const { return (m_bits & tokenTypeMask) == HeapCellToken; }
bool isOpaqueRoot() const { return (m_bits & tokenTypeMask) == OpaqueRootToken; }
bool isRootMarkReason() const { return (m_bits & tokenTypeMask) == RootMarkReasonToken; }
uintptr_t m_bits { 0 };
};
class ReferrerContext {
public:
ReferrerContext(AbstractSlotVisitor&, ReferrerToken);
ReferrerContext(AbstractSlotVisitor&, OpaqueRootTag);
~ReferrerContext();
ReferrerToken referrer() const { return m_referrer; }
void setReferrer(ReferrerToken referrer)
{
ASSERT(!m_referrer);
m_referrer = referrer;
}
bool isOpaqueRootContext() const { return m_isOpaqueRootContext; }
private:
AbstractSlotVisitor& m_visitor;
ReferrerToken m_referrer;
ReferrerContext* m_previous;
bool m_isOpaqueRootContext { false };
};
class SuppressGCVerifierScope {
public:
SuppressGCVerifierScope(AbstractSlotVisitor& visitor)
: m_visitor(visitor)
{
ASSERT(!m_visitor.m_suppressVerifier);
m_visitor.m_suppressVerifier = true;
}
~SuppressGCVerifierScope() { m_visitor.m_suppressVerifier = false; }
private:
AbstractSlotVisitor& m_visitor;
};
class DefaultMarkingViolationAssertionScope {
public:
DefaultMarkingViolationAssertionScope(AbstractSlotVisitor&) { }
};
virtual ~AbstractSlotVisitor() = default;
MarkStackArray& collectorMarkStack() { return m_collectorStack; }
MarkStackArray& mutatorMarkStack() { return m_mutatorStack; }
const MarkStackArray& collectorMarkStack() const { return m_collectorStack; }
const MarkStackArray& mutatorMarkStack() const { return m_mutatorStack; }
bool isEmpty() { return m_collectorStack.isEmpty() && m_mutatorStack.isEmpty(); }
VM& vm();
const VM& vm() const;
Heap* heap() const;
virtual void append(const ConservativeRoots&) = 0;
template<typename T, typename Traits> void append(const WriteBarrierBase<T, Traits>&);
template<typename T, typename Traits> void appendHidden(const WriteBarrierBase<T, Traits>&);
template<typename Iterator> void append(Iterator begin , Iterator end);
ALWAYS_INLINE void appendValues(const WriteBarrierBase<Unknown, RawValueTraits<Unknown>>*, size_t count);
ALWAYS_INLINE void appendValuesHidden(const WriteBarrierBase<Unknown, RawValueTraits<Unknown>>*, size_t count);
// These don't require you to prove that you have a WriteBarrier<>. That makes sense
// for:
//
// - roots.
// - sophisticated data structures that barrier through other means (like DFG::Plan and
// friends).
//
// If you are not a root and you don't know what kind of barrier you have, then you
// shouldn't call these methods.
ALWAYS_INLINE void appendUnbarriered(JSValue);
ALWAYS_INLINE void appendUnbarriered(JSValue*, size_t);
virtual void appendUnbarriered(JSCell*) = 0;
inline bool addOpaqueRoot(void*); // Returns true if the root was new.
inline bool containsOpaqueRoot(void*) const;
void setIgnoreNewOpaqueRoots(bool value) { m_ignoreNewOpaqueRoots = value; }
virtual bool isFirstVisit() const = 0;
virtual bool isMarked(const void*) const = 0;
virtual bool isMarked(MarkedBlock&, HeapCell*) const = 0;
virtual bool isMarked(PreciseAllocation&, HeapCell*) const = 0;
template<typename T>
void append(const Weak<T>&);
ALWAYS_INLINE void appendHiddenUnbarriered(JSValue);
virtual void appendHiddenUnbarriered(JSCell*) = 0;
size_t visitCount() const { return m_visitCount; }
void addToVisitCount(size_t value) { m_visitCount += value; }
virtual void addParallelConstraintTask(RefPtr<SharedTask<void(AbstractSlotVisitor&)>>) = 0;
virtual void addParallelConstraintTask(RefPtr<SharedTask<void(SlotVisitor&)>>) = 0;
virtual void markAuxiliary(const void* base) = 0;
virtual void reportExtraMemoryVisited(size_t) = 0;
#if ENABLE(RESOURCE_USAGE)
virtual void reportExternalMemoryVisited(size_t) = 0;
#endif
virtual void dump(PrintStream&) const = 0;
RootMarkReason rootMarkReason() const { return m_rootMarkReason; }
void setRootMarkReason(RootMarkReason reason) { m_rootMarkReason = reason; }
virtual bool mutatorIsStopped() const = 0;
virtual void didRace(const VisitRaceKey&) = 0;
void didRace(JSCell* cell, const char* reason) { didRace(VisitRaceKey(cell, reason)); }
virtual void visitAsConstraint(const JSCell*) = 0;
const char* codeName() const { return m_codeName.data(); }
protected:
inline AbstractSlotVisitor(Heap&, CString codeName, ConcurrentPtrHashSet&);
virtual void didAddOpaqueRoot(void*) { }
virtual void didFindOpaqueRoot(void*) { }
ReferrerToken referrer() const;
void reset();
MarkStackArray m_collectorStack;
MarkStackArray m_mutatorStack;
size_t m_visitCount { 0 };
Heap& m_heap;
ReferrerContext* m_context { nullptr };
CString m_codeName;
MarkingConstraint* m_currentConstraint { nullptr };
MarkingConstraintSolver* m_currentSolver { nullptr };
ConcurrentPtrHashSet& m_opaqueRoots;
RootMarkReason m_rootMarkReason { RootMarkReason::None };
bool m_suppressVerifier { false };
bool m_ignoreNewOpaqueRoots { false }; // Useful as a debugging mode.
bool m_needsExtraOpaqueRootHandling { false };
friend class MarkingConstraintSolver;
};
template<typename Visitor>
class SetRootMarkReasonScope {
public:
SetRootMarkReasonScope(Visitor& visitor, RootMarkReason reason)
: m_visitor(visitor)
, m_previousReason(visitor.rootMarkReason())
, m_context(visitor, reason)
{
m_visitor.setRootMarkReason(reason);
}
~SetRootMarkReasonScope()
{
m_visitor.setRootMarkReason(m_previousReason);
}
private:
Visitor& m_visitor;
RootMarkReason m_previousReason;
typename Visitor::ReferrerContext m_context;
};
} // namespace JSC