#ifndef V8_HEAP_MEMORY_CHUNK_H_
#define V8_HEAP_MEMORY_CHUNK_H_
#include "src/base/build_config.h"
#include "src/base/hashing.h"
#include "src/init/isolate-group.h"
#if V8_ENABLE_STICKY_MARK_BITS_BOOL
#define UNREACHABLE_WITH_STICKY_MARK_BITS() UNREACHABLE()
#else
#define UNREACHABLE_WITH_STICKY_MARK_BITS()
#endif
namespace v8 {
namespace internal {
namespace debug_helper_internal {
class ReadStringVisitor;
}
namespace compiler::turboshaft {
template <typename Next>
class AssemblerOpInterface;
}
class CodeStubAssembler;
class ExternalReference;
class Heap;
class LargePageMetadata;
class MemoryChunkMetadata;
class MutablePageMetadata;
class PageMetadata;
class ReadOnlyPageMetadata;
template <typename T>
class Tagged;
class TestDebugHelper;
class V8_EXPORT_PRIVATE MemoryChunk final {
public:
enum Flag : uintptr_t {
NO_FLAGS = 0u,
IN_WRITABLE_SHARED_SPACE = 1u << 0,
POINTERS_TO_HERE_ARE_INTERESTING = 1u << 1,
POINTERS_FROM_HERE_ARE_INTERESTING = 1u << 2,
FROM_PAGE = 1u << 3,
TO_PAGE = 1u << 4,
INCREMENTAL_MARKING = 1u << 5,
READ_ONLY_HEAP = 1u << 6,
BLACK_ALLOCATED = 1u << 7,
LARGE_PAGE = 1u << 8,
EVACUATION_CANDIDATE = 1u << 9,
NEW_SPACE_BELOW_AGE_MARK = 1u << 10,
#if V8_ENABLE_STICKY_MARK_BITS_BOOL
STICKY_MARK_BIT_CONTAINS_ONLY_OLD = 1u << 11,
STICKY_MARK_BIT_IS_MAJOR_GC_IN_PROGRESS = 1u << 12,
#endif
};
using MainThreadFlags = base::Flags<Flag, uintptr_t>;
static constexpr MainThreadFlags kAllFlagsMask = ~MainThreadFlags(NO_FLAGS);
static constexpr MainThreadFlags kPointersToHereAreInterestingMask =
POINTERS_TO_HERE_ARE_INTERESTING;
static constexpr MainThreadFlags kPointersFromHereAreInterestingMask =
POINTERS_FROM_HERE_ARE_INTERESTING;
static constexpr MainThreadFlags kEvacuationCandidateMask =
EVACUATION_CANDIDATE;
static constexpr MainThreadFlags kIsInYoungGenerationMask =
MainThreadFlags(FROM_PAGE) | MainThreadFlags(TO_PAGE);
static constexpr MainThreadFlags kIsInReadOnlyHeapMask = READ_ONLY_HEAP;
static constexpr MainThreadFlags kIsLargePageMask = LARGE_PAGE;
static constexpr MainThreadFlags kInSharedHeap = IN_WRITABLE_SHARED_SPACE;
static constexpr MainThreadFlags kIncrementalMarking = INCREMENTAL_MARKING;
static constexpr MainThreadFlags kSkipEvacuationSlotsRecordingMask =
MainThreadFlags(kEvacuationCandidateMask) |
MainThreadFlags(kIsInYoungGenerationMask);
#if V8_ENABLE_STICKY_MARK_BITS_BOOL
static constexpr MainThreadFlags kIsOnlyOldOrMajorGCInProgressMask =
MainThreadFlags(STICKY_MARK_BIT_CONTAINS_ONLY_OLD) |
MainThreadFlags(STICKY_MARK_BIT_IS_MAJOR_GC_IN_PROGRESS);
#endif
MemoryChunk(MainThreadFlags flags, MemoryChunkMetadata* metadata);
V8_INLINE Address address() const { return reinterpret_cast<Address>(this); }
static constexpr Address BaseAddress(Address a) {
return a & ~kAlignmentMask;
}
V8_INLINE static MemoryChunk* FromAddress(Address addr) {
return reinterpret_cast<MemoryChunk*>(BaseAddress(addr));
}
template <typename HeapObject>
V8_INLINE static MemoryChunk* FromHeapObject(Tagged<HeapObject> object) {
return FromAddress(object.ptr());
}
V8_INLINE MemoryChunkMetadata* Metadata(const Isolate* isolate);
V8_INLINE const MemoryChunkMetadata* Metadata(const Isolate* isolate) const;
V8_INLINE MemoryChunkMetadata* Metadata();
V8_INLINE const MemoryChunkMetadata* Metadata() const;
V8_INLINE MemoryChunkMetadata* MetadataNoIsolateCheck();
V8_INLINE const MemoryChunkMetadata* MetadataNoIsolateCheck() const;
V8_INLINE bool IsMarking() const { return IsFlagSet(INCREMENTAL_MARKING); }
V8_INLINE bool InWritableSharedSpace() const {
return IsFlagSet(IN_WRITABLE_SHARED_SPACE);
}
V8_INLINE bool IsBlackAllocatedPage() const {
return IsFlagSet(BLACK_ALLOCATED);
}
V8_INLINE bool ContainsOnlyOldObjects() const {
#if V8_ENABLE_STICKY_MARK_BITS_BOOL
DCHECK(v8_flags.sticky_mark_bits);
return IsFlagSet(STICKY_MARK_BIT_CONTAINS_ONLY_OLD);
#else
UNREACHABLE();
#endif
}
V8_INLINE bool InYoungGeneration() const {
UNREACHABLE_WITH_STICKY_MARK_BITS();
constexpr uintptr_t kYoungGenerationMask = FROM_PAGE | TO_PAGE;
return GetFlags() & kYoungGenerationMask;
}
V8_INLINE bool IsYoungOrSharedChunk() const {
constexpr uintptr_t kYoungOrSharedChunkMask =
FROM_PAGE | TO_PAGE | IN_WRITABLE_SHARED_SPACE;
return GetFlags() & kYoungOrSharedChunkMask;
}
V8_INLINE MainThreadFlags GetFlags() const {
return untrusted_main_thread_flags_;
}
void InitializationMemoryFence();
#ifdef THREAD_SANITIZER
void SynchronizedLoad() const;
bool InReadOnlySpace() const;
#else
V8_INLINE bool InReadOnlySpace() const { return IsFlagSet(READ_ONLY_HEAP); }
#endif
#ifdef V8_ENABLE_SANDBOX
bool SandboxSafeInReadOnlySpace() const;
#endif
V8_INLINE bool IsEvacuationCandidate() const;
bool ShouldSkipEvacuationSlotRecording() const {
return ((GetFlags() & kSkipEvacuationSlotsRecordingMask) != 0);
}
bool IsFromPage() const {
UNREACHABLE_WITH_STICKY_MARK_BITS();
return IsFlagSet(FROM_PAGE);
}
bool IsToPage() const {
UNREACHABLE_WITH_STICKY_MARK_BITS();
return IsFlagSet(TO_PAGE);
}
bool IsLargePage() const { return IsFlagSet(LARGE_PAGE); }
bool InNewSpace() const { return InYoungGeneration() && !IsLargePage(); }
bool InNewSpaceBelowAgeMark() const {
return IsFlagSet(NEW_SPACE_BELOW_AGE_MARK);
}
bool InNewLargeObjectSpace() const {
return InYoungGeneration() && IsLargePage();
}
bool IsOnlyOldOrMajorMarkingOn() const {
#if V8_ENABLE_STICKY_MARK_BITS_BOOL
DCHECK(v8_flags.sticky_mark_bits);
return GetFlags() & kIsOnlyOldOrMajorGCInProgressMask;
#else
UNREACHABLE();
#endif
}
bool PointersToHereAreInteresting() const {
return IsFlagSet(POINTERS_TO_HERE_ARE_INTERESTING);
}
bool PointersFromHereAreInteresting() const {
return IsFlagSet(POINTERS_FROM_HERE_ARE_INTERESTING);
}
V8_INLINE static constexpr bool IsAligned(Address address) {
return (address & kAlignmentMask) == 0;
}
static intptr_t GetAlignmentForAllocation() { return kAlignment; }
static constexpr intptr_t GetAlignmentMaskForAssembler() {
return kAlignmentMask;
}
static constexpr uint32_t AddressToOffset(Address address) {
return static_cast<uint32_t>(address) & kAlignmentMask;
}
#ifdef DEBUG
size_t Offset(Address addr) const;
size_t OffsetMaybeOutOfRange(Address addr) const;
#else
size_t Offset(Address addr) const { return addr - address(); }
size_t OffsetMaybeOutOfRange(Address addr) const { return Offset(addr); }
#endif
#ifdef V8_ENABLE_SANDBOX
static void ClearMetadataPointer(MemoryChunkMetadata* metadata);
#endif
private:
V8_INLINE bool IsFlagSet(Flag flag) const {
return untrusted_main_thread_flags_ & flag;
}
static constexpr intptr_t FlagsOffset() {
return offsetof(MemoryChunk, untrusted_main_thread_flags_);
}
static constexpr intptr_t kAlignment =
(static_cast<uintptr_t>(1) << kPageSizeBits);
static constexpr intptr_t kAlignmentMask = kAlignment - 1;
#ifdef V8_ENABLE_SANDBOX
#ifndef V8_EXTERNAL_CODE_SPACE
#error The global metadata pointer table requires a single external code space.
#endif
static constexpr intptr_t MetadataIndexOffset() {
return offsetof(MemoryChunk, metadata_index_);
}
static uint32_t MetadataTableIndex(Address chunk_address);
V8_INLINE static IsolateGroup::MemoryChunkMetadataTableEntry*
MetadataTableAddress() {
return IsolateGroup::current()->metadata_pointer_table();
}
friend class debug_helper_internal::ReadStringVisitor;
friend class ExternalReference;
friend class TestDebugHelper;
#else
static constexpr intptr_t MetadataOffset() {
return offsetof(MemoryChunk, metadata_);
}
#endif
template <bool check_isolate>
MemoryChunkMetadata* MetadataImpl(const Isolate* isolate);
template <bool check_isolate>
const MemoryChunkMetadata* MetadataImpl(const Isolate* isolate) const;
MainThreadFlags untrusted_main_thread_flags_;
#ifdef V8_ENABLE_SANDBOX
uint32_t metadata_index_;
#else
MemoryChunkMetadata* metadata_;
#endif
friend class MutablePageMetadata;
friend class CodeStubAssembler;
friend class MacroAssembler;
template <typename Next>
friend class compiler::turboshaft::AssemblerOpInterface;
friend class IsolateGroup;
friend class MemoryChunkMetadata;
};
DEFINE_OPERATORS_FOR_FLAGS(MemoryChunk::MainThreadFlags)
}
namespace base {
template <>
struct hash<const i::MemoryChunk*> {
V8_INLINE size_t operator()(const i::MemoryChunk* chunk) const {
return static_cast<v8::internal::Tagged_t>(
reinterpret_cast<uintptr_t>(chunk)) >>
kPageSizeBits;
}
};
template <>
struct hash<i::MemoryChunk*> {
V8_INLINE size_t operator()(i::MemoryChunk* chunk) const {
return hash<const i::MemoryChunk*>()(chunk);
}
};
}
}
#undef UNREACHABLE_WITH_STICKY_MARK_BITS
#endif