v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
memory-chunk.h
Go to the documentation of this file.
1// Copyright 2023 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_HEAP_MEMORY_CHUNK_H_
6#define V8_HEAP_MEMORY_CHUNK_H_
7
9#include "src/base/hashing.h"
10#include "src/flags/flags.h"
13
14#if V8_ENABLE_STICKY_MARK_BITS_BOOL
15#define UNREACHABLE_WITH_STICKY_MARK_BITS() UNREACHABLE()
16#else
17#define UNREACHABLE_WITH_STICKY_MARK_BITS()
18#endif
19
20namespace v8 {
21namespace internal {
22
23namespace debug_helper_internal {
24class ReadStringVisitor;
25} // namespace debug_helper_internal
26
27class Heap;
28class MemoryChunkMetadata;
29class ReadOnlyPageMetadata;
30class PageMetadata;
31class LargePageMetadata;
32class CodeStubAssembler;
33class ExternalReference;
34template <typename T>
35class Tagged;
36class TestDebugHelper;
37
39
41 public:
42 // All possible flags that can be set on a page. While the value of flags
43 // doesn't matter in principle, keep flags used in the write barrier together
44 // in order to have dense page flag checks in the write barrier.
45 enum Flag : uintptr_t {
46 NO_FLAGS = 0u,
47
48 // This page belongs to a shared heap.
49 IN_WRITABLE_SHARED_SPACE = 1u << 0,
50
51 // These two flags are used in the write barrier to catch "interesting"
52 // references.
53 POINTERS_TO_HERE_ARE_INTERESTING = 1u << 1,
54 POINTERS_FROM_HERE_ARE_INTERESTING = 1u << 2,
55
56 // A page in the from-space or a young large page that was not scavenged
57 // yet.
58 FROM_PAGE = 1u << 3,
59 // A page in the to-space or a young large page that was scavenged.
60 TO_PAGE = 1u << 4,
61
62 // |INCREMENTAL_MARKING|: Indicates whether incremental marking is currently
63 // enabled.
64 INCREMENTAL_MARKING = 1u << 5,
65
66 // The memory chunk belongs to the read-only heap and does not participate
67 // in garbage collection. This is used instead of owner for identity
68 // checking since read-only chunks have no owner once they are detached.
69 READ_ONLY_HEAP = 1u << 6,
70
71 // Used in young generation checks. When sticky mark-bits are enabled and
72 // major GC in progress, treat all objects as old.
73 IS_MAJOR_GC_IN_PROGRESS = 1u << 7,
74
75 // Used to mark chunks belonging to spaces that do not suppor young gen
76 // allocations. Such chunks can never contain any young objects.
77 CONTAINS_ONLY_OLD = 1u << 8,
78
79 // Page was allocated during major incremental marking. May only contain old
80 // objects.
81 BLACK_ALLOCATED = 1u << 9,
82
83 // ----------------------------------------------------------------
84 // Values below here are not critical for the heap write barrier.
85
86 LARGE_PAGE = 1u << 10,
87 EVACUATION_CANDIDATE = 1u << 11,
88 NEVER_EVACUATE = 1u << 12,
89
90 // |PAGE_NEW_OLD_PROMOTION|: A page tagged with this flag has been promoted
91 // from new to old space during evacuation.
92 PAGE_NEW_OLD_PROMOTION = 1u << 13,
93
94 // This flag is intended to be used for testing. Works only when both
95 // v8_flags.stress_compaction and
96 // v8_flags.manual_evacuation_candidates_selection are set. It forces the
97 // page to become an evacuation candidate at next candidates selection
98 // cycle.
99 FORCE_EVACUATION_CANDIDATE_FOR_TESTING = 1u << 14,
100
101 // This flag is intended to be used for testing.
102 NEVER_ALLOCATE_ON_PAGE = 1u << 15,
103
104 // The memory chunk is already logically freed, however the actual freeing
105 // still has to be performed.
106 PRE_FREED = 1u << 16,
107
108 // |COMPACTION_WAS_ABORTED|: Indicates that the compaction in this page
109 // has been aborted and needs special handling by the sweeper.
110 COMPACTION_WAS_ABORTED = 1u << 17,
111
112 NEW_SPACE_BELOW_AGE_MARK = 1u << 18,
113
114 // The memory chunk freeing bookkeeping has been performed but the chunk has
115 // not yet been freed.
116 UNREGISTERED = 1u << 19,
117
118 // The memory chunk is pinned in memory and can't be moved. This is likely
119 // because there exists a potential pointer to somewhere in the chunk which
120 // can't be updated.
121 PINNED = 1u << 20,
122
123 // A Page with code objects.
124 IS_EXECUTABLE = 1u << 21,
125
126 // The memory chunk belongs to the trusted space. When the sandbox is
127 // enabled, the trusted space is located outside of the sandbox and so its
128 // content cannot be corrupted by an attacker.
129 IS_TRUSTED = 1u << 22,
130
131 // A quarantined page that contains objects reachable from stack during a
132 // scavenge. Quarantined pages are not used for further allocations in new
133 // space (to make it easier to keep track of the intermediate generation).
134 // This flag should only ever be set during a scavenge cycle.
135 IS_QUARANTINED = 1u << 23,
136
137 // A new space page that will be promoted to old space by the end of the GC.
138 // This flag should only ever be set during a scavenge cycle.
139 WILL_BE_PROMOTED = 1u << 24,
140 };
141
143
144 static constexpr MainThreadFlags kAllFlagsMask = ~MainThreadFlags(NO_FLAGS);
145 static constexpr MainThreadFlags kPointersToHereAreInterestingMask =
146 POINTERS_TO_HERE_ARE_INTERESTING;
147 static constexpr MainThreadFlags kPointersFromHereAreInterestingMask =
148 POINTERS_FROM_HERE_ARE_INTERESTING;
149 static constexpr MainThreadFlags kEvacuationCandidateMask =
150 EVACUATION_CANDIDATE;
151 static constexpr MainThreadFlags kIsInYoungGenerationMask =
152 MainThreadFlags(FROM_PAGE) | MainThreadFlags(TO_PAGE);
153 static constexpr MainThreadFlags kIsInReadOnlyHeapMask = READ_ONLY_HEAP;
154 static constexpr MainThreadFlags kIsLargePageMask = LARGE_PAGE;
155 static constexpr MainThreadFlags kInSharedHeap = IN_WRITABLE_SHARED_SPACE;
156 static constexpr MainThreadFlags kIncrementalMarking = INCREMENTAL_MARKING;
157 static constexpr MainThreadFlags kSkipEvacuationSlotsRecordingMask =
158 MainThreadFlags(kEvacuationCandidateMask) |
159 MainThreadFlags(kIsInYoungGenerationMask);
160 static constexpr MainThreadFlags kIsOnlyOldOrMajorGCInProgressMask =
161 MainThreadFlags(CONTAINS_ONLY_OLD) |
162 MainThreadFlags(IS_MAJOR_GC_IN_PROGRESS);
163
165
166 V8_INLINE Address address() const { return reinterpret_cast<Address>(this); }
167
168 static constexpr Address BaseAddress(Address a) {
169 // If this changes, we also need to update
170 // CodeStubAssembler::MemoryChunkFromAddress and
171 // MacroAssembler::MemoryChunkHeaderFromObject
172 return a & ~kAlignmentMask;
173 }
174
175 V8_INLINE static MemoryChunk* FromAddress(Address addr) {
176 return reinterpret_cast<MemoryChunk*>(BaseAddress(addr));
177 }
178
179 template <typename HeapObject>
181 return FromAddress(object.ptr());
182 }
183
184 V8_INLINE MemoryChunkMetadata* Metadata();
185
186 V8_INLINE const MemoryChunkMetadata* Metadata() const;
187
188 V8_INLINE bool IsFlagSet(Flag flag) const {
189 return main_thread_flags_ & flag;
190 }
191
192 V8_INLINE bool IsMarking() const { return IsFlagSet(INCREMENTAL_MARKING); }
193
195 return IsFlagSet(IN_WRITABLE_SHARED_SPACE);
196 }
197
200 constexpr uintptr_t kYoungGenerationMask = FROM_PAGE | TO_PAGE;
201 return GetFlags() & kYoungGenerationMask;
202 }
203
204 // Checks whether chunk is either in young gen or shared heap.
206 constexpr uintptr_t kYoungOrSharedChunkMask =
207 FROM_PAGE | TO_PAGE | IN_WRITABLE_SHARED_SPACE;
208 return GetFlags() & kYoungOrSharedChunkMask;
209 }
210
211 void SetFlagSlow(Flag flag);
212 void ClearFlagSlow(Flag flag);
213
214 V8_INLINE MainThreadFlags GetFlags() const { return main_thread_flags_; }
215
216 V8_INLINE void SetFlagUnlocked(Flag flag) { main_thread_flags_ |= flag; }
218 main_thread_flags_ = main_thread_flags_.without(flag);
219 }
220 // Set or clear multiple flags at a time. `mask` indicates which flags are
221 // should be replaced with new `flags`.
223 main_thread_flags_ &= ~flags;
224 }
226 MainThreadFlags mask = kAllFlagsMask) {
227 main_thread_flags_ = (main_thread_flags_ & ~mask) | (flags & mask);
228 }
229
231 return SetFlagUnlocked(flag);
232 }
234 return ClearFlagUnlocked(flag);
235 }
237 MainThreadFlags mask = kAllFlagsMask) {
238 return SetFlagsUnlocked(flags, mask);
239 }
241 return ClearFlagsUnlocked(flags);
242 }
244 SetFlagUnlocked(IS_MAJOR_GC_IN_PROGRESS);
245 }
247 ClearFlagUnlocked(IS_MAJOR_GC_IN_PROGRESS);
248 }
249
250 V8_INLINE Heap* GetHeap();
251
252 // Emits a memory barrier. For TSAN builds the other thread needs to perform
253 // MemoryChunk::SynchronizedLoad() to simulate the barrier.
254 void InitializationMemoryFence();
255
256#ifdef THREAD_SANITIZER
257 void SynchronizedLoad() const;
258 bool InReadOnlySpace() const;
259#else
260 V8_INLINE bool InReadOnlySpace() const { return IsFlagSet(READ_ONLY_HEAP); }
261#endif
262
263#ifdef V8_ENABLE_SANDBOX
264 // Flags are stored in the page header and are not safe to rely on for sandbox
265 // checks. This alternative version will check if the page is read-only
266 // without relying on the inline flag.
267 bool SandboxSafeInReadOnlySpace() const;
268#endif
269
270 V8_INLINE bool InCodeSpace() const { return IsFlagSet(IS_EXECUTABLE); }
271
272 V8_INLINE bool InTrustedSpace() const { return IsFlagSet(IS_TRUSTED); }
273
274 bool NeverEvacuate() const { return IsFlagSet(NEVER_EVACUATE); }
275 void MarkNeverEvacuate() { SetFlagSlow(NEVER_EVACUATE); }
276
277 bool CanAllocate() const {
278 return !IsEvacuationCandidate() && !IsFlagSet(NEVER_ALLOCATE_ON_PAGE);
279 }
280
282 DCHECK(!(IsFlagSet(NEVER_EVACUATE) && IsFlagSet(EVACUATION_CANDIDATE)));
283 return IsFlagSet(EVACUATION_CANDIDATE);
284 }
285
287 MainThreadFlags flags = GetFlags();
288 return ((flags & kSkipEvacuationSlotsRecordingMask) != 0) &&
289 ((flags & COMPACTION_WAS_ABORTED) == 0);
290 }
291
293 return IsFlagSet(IS_EXECUTABLE) ? EXECUTABLE : NOT_EXECUTABLE;
294 }
295
296 bool IsFromPage() const {
298 return IsFlagSet(FROM_PAGE);
299 }
300 bool IsToPage() const {
302 return IsFlagSet(TO_PAGE);
303 }
304 bool IsLargePage() const { return IsFlagSet(LARGE_PAGE); }
305 bool InNewSpace() const { return InYoungGeneration() && !IsLargePage(); }
307 return InYoungGeneration() && IsLargePage();
308 }
309 bool IsPinned() const { return IsFlagSet(PINNED); }
311 return GetFlags() & kIsOnlyOldOrMajorGCInProgressMask;
312 }
313
314 bool IsQuarantined() const { return IsFlagSet(IS_QUARANTINED); }
315
316 V8_INLINE static constexpr bool IsAligned(Address address) {
317 return (address & kAlignmentMask) == 0;
318 }
319
320 static MainThreadFlags OldGenerationPageFlags(MarkingMode marking_mode,
321 AllocationSpace space);
322 static MainThreadFlags YoungGenerationPageFlags(MarkingMode marking_mode);
323
324 void SetOldGenerationPageFlags(MarkingMode marking_mode,
325 AllocationSpace space);
326 void SetYoungGenerationPageFlags(MarkingMode marking_mode);
327
328#ifdef DEBUG
329 bool IsTrusted() const;
330#else
331 bool IsTrusted() const { return IsFlagSet(IS_TRUSTED); }
332#endif
333
334 static intptr_t GetAlignmentForAllocation() { return kAlignment; }
335 // The macro and code stub assemblers need access to the alignment mask to
336 // implement functionality from this class. In particular, this is used to
337 // implement the header lookups and to calculate the object offsets in the
338 // page.
339 static constexpr intptr_t GetAlignmentMaskForAssembler() {
340 return kAlignmentMask;
341 }
342
343 static constexpr uint32_t AddressToOffset(Address address) {
344 return static_cast<uint32_t>(address) & kAlignmentMask;
345 }
346
347#ifdef DEBUG
348 size_t Offset(Address addr) const;
349 // RememberedSetOperations take an offset to an end address that can be behind
350 // the allocated memory.
351 size_t OffsetMaybeOutOfRange(Address addr) const;
352#else
353 size_t Offset(Address addr) const { return addr - address(); }
354 size_t OffsetMaybeOutOfRange(Address addr) const { return Offset(addr); }
355#endif
356
357#ifdef V8_ENABLE_SANDBOX
358 static void ClearMetadataPointer(MemoryChunkMetadata* metadata);
359#endif
360
361 private:
362 // Keep offsets and masks private to only expose them with matching friend
363 // declarations.
364 static constexpr intptr_t FlagsOffset() {
365 return offsetof(MemoryChunk, main_thread_flags_);
366 }
367
368 static constexpr intptr_t kAlignment =
369 (static_cast<uintptr_t>(1) << kPageSizeBits);
370 static constexpr intptr_t kAlignmentMask = kAlignment - 1;
371
372#ifdef V8_ENABLE_SANDBOX
373#ifndef V8_EXTERNAL_CODE_SPACE
374#error The global metadata pointer table requires a single external code space.
375#endif
376
377 static constexpr intptr_t MetadataIndexOffset() {
378 return offsetof(MemoryChunk, metadata_index_);
379 }
380
381 V8_INLINE static MemoryChunkMetadata* FromIndex(uint32_t index);
382 static uint32_t MetadataTableIndex(Address chunk_address);
383
384 V8_INLINE static MemoryChunkMetadata** MetadataTableAddress() {
385 return IsolateGroup::current()->metadata_pointer_table();
386 }
387
388 // For MetadataIndexOffset().
389 friend class debug_helper_internal::ReadStringVisitor;
390 // For MetadataTableAddress().
391 friend class ExternalReference;
392 friend class TestDebugHelper;
393
394#else // !V8_ENABLE_SANDBOX
395
396 static constexpr intptr_t MetadataOffset() {
397 return offsetof(MemoryChunk, metadata_);
398 }
399
400#endif // !V8_ENABLE_SANDBOX
401
402 // Flags that are only mutable from the main thread when no concurrent
403 // component (e.g. marker, sweeper, compilation, allocation) is running.
405
406#ifdef V8_ENABLE_SANDBOX
407 uint32_t metadata_index_;
408#else
410#endif
411
412 // For kMetadataPointerTableSizeMask, FlagsOffset(), MetadataIndexOffset(),
413 // MetadataOffset().
414 friend class CodeStubAssembler;
415 friend class MacroAssembler;
416};
417
419
420} // namespace internal
421
422namespace base {
423
424// Define special hash function for chunk pointers, to be used with std data
425// structures, e.g.
426// std::unordered_set<MemoryChunk*, base::hash<MemoryChunk*>
427// This hash function discards the trailing zero bits (chunk alignment).
428// Notice that, when pointer compression is enabled, it also discards the
429// cage base.
430template <>
431struct hash<const i::MemoryChunk*> {
432 V8_INLINE size_t operator()(const i::MemoryChunk* chunk) const {
433 return static_cast<v8::internal::Tagged_t>(
434 reinterpret_cast<uintptr_t>(chunk)) >>
436 }
437};
438
439template <>
440struct hash<i::MemoryChunk*> {
441 V8_INLINE size_t operator()(i::MemoryChunk* chunk) const {
442 return hash<const i::MemoryChunk*>()(chunk);
443 }
444};
445
446} // namespace base
447
448} // namespace v8
449
450#undef UNREACHABLE_WITH_STICKY_MARK_BITS
451
452#endif // V8_HEAP_MEMORY_CHUNK_H_
#define DEFINE_OPERATORS_FOR_FLAGS(Type)
Definition flags.h:100
constexpr int kPageSizeBits
V8_INLINE bool InTrustedSpace() const
static constexpr uint32_t AddressToOffset(Address address)
MainThreadFlags main_thread_flags_
V8_INLINE bool InWritableSharedSpace() const
V8_INLINE bool IsYoungOrSharedChunk() const
static constexpr Address BaseAddress(Address a)
bool IsEvacuationCandidate() const
bool IsOnlyOldOrMajorMarkingOn() const
static constexpr intptr_t FlagsOffset()
V8_INLINE void SetMajorGCInProgress()
V8_INLINE void SetFlagNonExecutable(Flag flag)
V8_INLINE void SetFlagUnlocked(Flag flag)
size_t OffsetMaybeOutOfRange(Address addr) const
static constexpr intptr_t MetadataOffset()
Executability executable() const
V8_INLINE bool IsFlagSet(Flag flag) const
V8_INLINE Address address() const
V8_INLINE void SetFlagsNonExecutable(MainThreadFlags flags, MainThreadFlags mask=kAllFlagsMask)
static V8_INLINE MemoryChunk * FromAddress(Address addr)
V8_INLINE void ClearFlagUnlocked(Flag flag)
V8_INLINE void SetFlagsUnlocked(MainThreadFlags flags, MainThreadFlags mask=kAllFlagsMask)
size_t Offset(Address addr) const
V8_INLINE void ResetMajorGCInProgress()
static constexpr intptr_t GetAlignmentMaskForAssembler()
V8_INLINE void ClearFlagsNonExecutable(MainThreadFlags flags)
V8_INLINE bool InYoungGeneration() const
V8_INLINE bool InCodeSpace() const
bool ShouldSkipEvacuationSlotRecording() const
V8_INLINE void ClearFlagNonExecutable(Flag flag)
static V8_INLINE MemoryChunk * FromHeapObject(Tagged< HeapObject > object)
bool InNewLargeObjectSpace() const
V8_INLINE bool InReadOnlySpace() const
MemoryChunkMetadata * metadata_
V8_INLINE void ClearFlagsUnlocked(MainThreadFlags flags)
V8_INLINE MainThreadFlags GetFlags() const
static V8_INLINE constexpr bool IsAligned(Address address)
V8_INLINE bool IsMarking() const
static intptr_t GetAlignmentForAllocation()
uint32_t const mask
#define UNREACHABLE_WITH_STICKY_MARK_BITS()
Tagged(T object) -> Tagged< T >
Address Tagged_t
Definition globals.h:547
#define DCHECK(condition)
Definition logging.h:482
#define V8_EXPORT_PRIVATE
Definition macros.h:460
V8_INLINE size_t operator()(const i::MemoryChunk *chunk) const
V8_INLINE size_t operator()(i::MemoryChunk *chunk) const
#define V8_INLINE
Definition v8config.h:500