Loading...
Searching...
No Matches
write-barrier.h
Go to the documentation of this file.
1// Copyright 2020 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 INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_
6#define INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_
7
8#include <cstddef>
9#include <cstdint>
10
11#include "cppgc/heap-handle.h"
12#include "cppgc/heap-state.h"
17#include "cppgc/platform.h"
19#include "cppgc/trace-trait.h"
20#include "v8config.h" // NOLINT(build/include_directory)
21
22#if defined(CPPGC_CAGED_HEAP)
25#endif
26
27namespace cppgc {
28
29class HeapHandle;
30
31namespace internal {
32
33#if defined(CPPGC_CAGED_HEAP)
34class WriteBarrierTypeForCagedHeapPolicy;
35#else // !CPPGC_CAGED_HEAP
36class WriteBarrierTypeForNonCagedHeapPolicy;
37#endif // !CPPGC_CAGED_HEAP
38
40 public:
41 enum class Type : uint8_t {
42 kNone,
43 kMarking,
44 kGenerational,
45 };
46
47 enum class GenerationalBarrierType : uint8_t {
48 kPreciseSlot,
49 kPreciseUncompressedSlot,
50 kImpreciseSlot,
51 };
52
53 struct Params {
54 HeapHandle* heap = nullptr;
55#if V8_ENABLE_CHECKS
56 Type type = Type::kNone;
57#endif // !V8_ENABLE_CHECKS
58#if defined(CPPGC_CAGED_HEAP)
59 uintptr_t slot_offset = 0;
60 uintptr_t value_offset = 0;
61#endif // CPPGC_CAGED_HEAP
62 };
63
64 enum class ValueMode {
65 kValuePresent,
66 kNoValuePresent,
67 };
68
69 // Returns the required write barrier for a given `slot` and `value`.
70 static V8_INLINE Type GetWriteBarrierType(const void* slot, const void* value,
71 Params& params);
72 // Returns the required write barrier for a given `slot` and `value`.
73 template <typename MemberStorage>
74 static V8_INLINE Type GetWriteBarrierType(const void* slot, MemberStorage,
75 Params& params);
76 // Returns the required write barrier for a given `slot`.
77 template <typename HeapHandleCallback>
78 static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params,
79 HeapHandleCallback callback);
80 // Returns the required write barrier for a given `value`.
81 static V8_INLINE Type GetWriteBarrierType(const void* value, Params& params);
82
83#ifdef CPPGC_SLIM_WRITE_BARRIER
84 // A write barrier that combines `GenerationalBarrier()` and
85 // `DijkstraMarkingBarrier()`. We only pass a single parameter here to clobber
86 // as few registers as possible.
87 template <WriteBarrierSlotType>
88 static V8_NOINLINE V8_PRESERVE_MOST void CombinedWriteBarrierSlow(
89 const void* slot);
90#endif // CPPGC_SLIM_WRITE_BARRIER
91
92 static V8_INLINE void DijkstraMarkingBarrier(const Params& params,
93 const void* object);
94 static V8_INLINE void DijkstraMarkingBarrierRange(
95 const Params& params, const void* first_element, size_t element_size,
96 size_t number_of_elements, TraceCallback trace_callback);
97 static V8_INLINE void SteeleMarkingBarrier(const Params& params,
98 const void* object);
99#if defined(CPPGC_YOUNG_GENERATION)
100 template <GenerationalBarrierType>
101 static V8_INLINE void GenerationalBarrier(const Params& params,
102 const void* slot);
103#else // !CPPGC_YOUNG_GENERATION
104 template <GenerationalBarrierType>
105 static V8_INLINE void GenerationalBarrier(const Params& params,
106 const void* slot){}
107#endif // CPPGC_YOUNG_GENERATION
108
109#if V8_ENABLE_CHECKS
110 static void CheckParams(Type expected_type, const Params& params);
111#else // !V8_ENABLE_CHECKS
112 static V8_INLINE void CheckParams(Type expected_type, const Params& params) {}
113#endif // !V8_ENABLE_CHECKS
114
115 // The FlagUpdater class allows cppgc internal to update
116 // |write_barrier_enabled_|.
117 class FlagUpdater;
118 static bool IsEnabled() { return write_barrier_enabled_.MightBeEntered(); }
119
120 private:
121 WriteBarrier() = delete;
122
123#if defined(CPPGC_CAGED_HEAP)
124 using WriteBarrierTypePolicy = WriteBarrierTypeForCagedHeapPolicy;
125#else // !CPPGC_CAGED_HEAP
126 using WriteBarrierTypePolicy = WriteBarrierTypeForNonCagedHeapPolicy;
127#endif // !CPPGC_CAGED_HEAP
128
129 static V8_NOINLINE V8_PRESERVE_MOST void DijkstraMarkingBarrierSlow(
130 const void* value);
131 static V8_NOINLINE V8_PRESERVE_MOST void
132 DijkstraMarkingBarrierSlowWithSentinelCheck(const void* value);
133 static V8_NOINLINE V8_PRESERVE_MOST void DijkstraMarkingBarrierRangeSlow(
134 HeapHandle& heap_handle, const void* first_element, size_t element_size,
135 size_t number_of_elements, TraceCallback trace_callback);
136 static V8_NOINLINE V8_PRESERVE_MOST void SteeleMarkingBarrierSlow(
137 const void* value);
138 static V8_NOINLINE V8_PRESERVE_MOST void
139 SteeleMarkingBarrierSlowWithSentinelCheck(const void* value);
140
141#if defined(CPPGC_YOUNG_GENERATION)
142 static V8_NOINLINE V8_PRESERVE_MOST void GenerationalBarrierSlow(
143 const CagedHeapLocalData& local_data, const AgeTable& age_table,
144 const void* slot, uintptr_t value_offset, HeapHandle* heap_handle);
145 static V8_NOINLINE V8_PRESERVE_MOST void
146 GenerationalBarrierForUncompressedSlotSlow(
147 const CagedHeapLocalData& local_data, const AgeTable& age_table,
148 const void* slot, uintptr_t value_offset, HeapHandle* heap_handle);
149 static V8_NOINLINE V8_PRESERVE_MOST void
150 GenerationalBarrierForSourceObjectSlow(const CagedHeapLocalData& local_data,
151 const void* object,
152 HeapHandle* heap_handle);
153#endif // CPPGC_YOUNG_GENERATION
154
155 static AtomicEntryFlag write_barrier_enabled_;
156};
157
158template <WriteBarrier::Type type>
160 if constexpr (type == WriteBarrier::Type::kNone) {
162 }
163#if V8_ENABLE_CHECKS
164 params.type = type;
165#endif // !V8_ENABLE_CHECKS
166 return type;
167}
168
169#if defined(CPPGC_CAGED_HEAP)
170class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final {
171 public:
172 template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
173 static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value,
174 WriteBarrier::Params& params,
175 HeapHandleCallback callback) {
176 return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
177 }
178
179 template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback,
180 typename MemberStorage>
181 static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value,
182 WriteBarrier::Params& params,
183 HeapHandleCallback callback) {
184 return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
185 }
186
187 template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
188 static V8_INLINE WriteBarrier::Type Get(const void* value,
189 WriteBarrier::Params& params,
190 HeapHandleCallback callback) {
191 return GetNoSlot(value, params, callback);
192 }
193
194 private:
195 WriteBarrierTypeForCagedHeapPolicy() = delete;
196
197 template <typename HeapHandleCallback>
198 static V8_INLINE WriteBarrier::Type GetNoSlot(const void* value,
199 WriteBarrier::Params& params,
200 HeapHandleCallback) {
201 const bool within_cage = CagedHeapBase::IsWithinCage(value);
202 if (!within_cage) return WriteBarrier::Type::kNone;
203
204 // We know that |value| points either within the normal page or to the
205 // beginning of large-page, so extract the page header by bitmasking.
206 BasePageHandle* page =
207 BasePageHandle::FromPayload(const_cast<void*>(value));
208
209 HeapHandle& heap_handle = page->heap_handle();
210 if (heap_handle.is_incremental_marking_in_progress()) [[unlikely]] {
211 return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
212 }
213
214 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
215 }
216
217 template <WriteBarrier::ValueMode value_mode>
218 struct ValueModeDispatch;
219};
220
221template <>
222struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch<
223 WriteBarrier::ValueMode::kValuePresent> {
224 template <typename HeapHandleCallback, typename MemberStorage>
225 static V8_INLINE WriteBarrier::Type Get(const void* slot,
226 MemberStorage storage,
227 WriteBarrier::Params& params,
228 HeapHandleCallback) {
229 if (!WriteBarrier::IsEnabled()) [[likely]] {
230 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
231 }
232
233 return BarrierEnabledGet(slot, storage.Load(), params);
234 }
235
236 template <typename HeapHandleCallback>
237 static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value,
238 WriteBarrier::Params& params,
239 HeapHandleCallback) {
240 if (!WriteBarrier::IsEnabled()) [[likely]] {
241 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
242 }
243
244 return BarrierEnabledGet(slot, value, params);
245 }
246
247 private:
248 static V8_INLINE WriteBarrier::Type BarrierEnabledGet(
249 const void* slot, const void* value, WriteBarrier::Params& params) {
250 const bool within_cage = CagedHeapBase::AreWithinCage(slot, value);
251 if (!within_cage) return WriteBarrier::Type::kNone;
252
253 // We know that |value| points either within the normal page or to the
254 // beginning of large-page, so extract the page header by bitmasking.
255 BasePageHandle* page =
256 BasePageHandle::FromPayload(const_cast<void*>(value));
257
258 HeapHandle& heap_handle = page->heap_handle();
259 if (!heap_handle.is_incremental_marking_in_progress()) [[likely]] {
260#if defined(CPPGC_YOUNG_GENERATION)
261 if (!heap_handle.is_young_generation_enabled()) {
262 return WriteBarrier::Type::kNone;
263 }
264 params.heap = &heap_handle;
265 params.slot_offset = CagedHeapBase::OffsetFromAddress(slot);
266 params.value_offset = CagedHeapBase::OffsetFromAddress(value);
267 return SetAndReturnType<WriteBarrier::Type::kGenerational>(params);
268#else // !CPPGC_YOUNG_GENERATION
269 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
270#endif // !CPPGC_YOUNG_GENERATION
271 }
272
273 // Use marking barrier.
274 params.heap = &heap_handle;
275 return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
276 }
277};
278
279template <>
280struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch<
281 WriteBarrier::ValueMode::kNoValuePresent> {
282 template <typename HeapHandleCallback>
283 static V8_INLINE WriteBarrier::Type Get(const void* slot, const void*,
284 WriteBarrier::Params& params,
285 HeapHandleCallback callback) {
286 if (!WriteBarrier::IsEnabled()) [[likely]] {
287 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
288 }
289
290 HeapHandle& handle = callback();
291#if defined(CPPGC_YOUNG_GENERATION)
292 if (!handle.is_incremental_marking_in_progress()) [[likely]] {
293 if (!handle.is_young_generation_enabled()) {
294 return WriteBarrier::Type::kNone;
295 }
296 params.heap = &handle;
297 // Check if slot is on stack.
298 if (!CagedHeapBase::IsWithinCage(slot)) [[unlikely]] {
299 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
300 }
301 params.slot_offset = CagedHeapBase::OffsetFromAddress(slot);
302 return SetAndReturnType<WriteBarrier::Type::kGenerational>(params);
303 }
304#else // !defined(CPPGC_YOUNG_GENERATION)
305 if (!handle.is_incremental_marking_in_progress()) [[unlikely]] {
306 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
307 }
308#endif // !defined(CPPGC_YOUNG_GENERATION)
309 params.heap = &handle;
310 return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
311 }
312};
313
314#endif // CPPGC_CAGED_HEAP
315
317 public:
318 template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
319 static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value,
320 WriteBarrier::Params& params,
321 HeapHandleCallback callback) {
322 return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
323 }
324
325 template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
326 static V8_INLINE WriteBarrier::Type Get(const void* slot, RawPointer value,
327 WriteBarrier::Params& params,
328 HeapHandleCallback callback) {
329 return ValueModeDispatch<value_mode>::Get(slot, value.Load(), params,
330 callback);
331 }
332
333 template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
334 static V8_INLINE WriteBarrier::Type Get(const void* value,
335 WriteBarrier::Params& params,
336 HeapHandleCallback callback) {
337 // The slot will never be used in `Get()` below.
338 return Get<WriteBarrier::ValueMode::kValuePresent>(nullptr, value, params,
339 callback);
340 }
341
342 private:
343 template <WriteBarrier::ValueMode value_mode>
344 struct ValueModeDispatch;
345
347};
348
349template <>
350struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch<
351 WriteBarrier::ValueMode::kValuePresent> {
352 template <typename HeapHandleCallback>
353 static V8_INLINE WriteBarrier::Type Get(const void*, const void* object,
354 WriteBarrier::Params& params,
355 HeapHandleCallback callback) {
356 // The following check covers nullptr as well as sentinel pointer.
357 if (object <= static_cast<void*>(kSentinelPointer)) {
358 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
359 }
360 if (!WriteBarrier::IsEnabled()) [[likely]] {
361 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
362 }
363 // We know that |object| is within the normal page or in the beginning of a
364 // large page, so extract the page header by bitmasking.
365 BasePageHandle* page =
366 BasePageHandle::FromPayload(const_cast<void*>(object));
367
368 HeapHandle& heap_handle = page->heap_handle();
369 if (heap_handle.is_incremental_marking_in_progress()) [[likely]] {
370 return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
371 }
372 return SetAndReturnType<WriteBarrier::Type::kNone>(params);
373 }
374};
375
376template <>
377struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch<
378 WriteBarrier::ValueMode::kNoValuePresent> {
379 template <typename HeapHandleCallback>
380 static V8_INLINE WriteBarrier::Type Get(const void*, const void*,
381 WriteBarrier::Params& params,
382 HeapHandleCallback callback) {
383 if (WriteBarrier::IsEnabled()) [[unlikely]] {
384 HeapHandle& handle = callback();
385 if (handle.is_incremental_marking_in_progress()) [[likely]] {
386 params.heap = &handle;
387 return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
388 }
389 }
391 }
392};
393
394// static
396 const void* slot, const void* value, WriteBarrier::Params& params) {
397 return WriteBarrierTypePolicy::Get<ValueMode::kValuePresent>(slot, value,
398 params, []() {});
399}
400
401// static
402template <typename MemberStorage>
404 const void* slot, MemberStorage value, WriteBarrier::Params& params) {
405 return WriteBarrierTypePolicy::Get<ValueMode::kValuePresent>(slot, value,
406 params, []() {});
407}
408
409// static
410template <typename HeapHandleCallback>
412 const void* slot, WriteBarrier::Params& params,
413 HeapHandleCallback callback) {
414 return WriteBarrierTypePolicy::Get<ValueMode::kNoValuePresent>(
415 slot, nullptr, params, callback);
416}
417
418// static
420 const void* value, WriteBarrier::Params& params) {
421 return WriteBarrierTypePolicy::Get<ValueMode::kValuePresent>(value, params,
422 []() {});
423}
424
425// static
427 const void* object) {
429#if defined(CPPGC_CAGED_HEAP)
430 // Caged heap already filters out sentinels.
431 DijkstraMarkingBarrierSlow(object);
432#else // !CPPGC_CAGED_HEAP
433 DijkstraMarkingBarrierSlowWithSentinelCheck(object);
434#endif // !CPPGC_CAGED_HEAP
435}
436
437// static
439 const void* first_element,
440 size_t element_size,
441 size_t number_of_elements,
442 TraceCallback trace_callback) {
444 DijkstraMarkingBarrierRangeSlow(*params.heap, first_element, element_size,
445 number_of_elements, trace_callback);
446}
447
448// static
450 const void* object) {
452#if defined(CPPGC_CAGED_HEAP)
453 // Caged heap already filters out sentinels.
454 SteeleMarkingBarrierSlow(object);
455#else // !CPPGC_CAGED_HEAP
456 SteeleMarkingBarrierSlowWithSentinelCheck(object);
457#endif // !CPPGC_CAGED_HEAP
458}
459
460#if defined(CPPGC_YOUNG_GENERATION)
461
462// static
463template <WriteBarrier::GenerationalBarrierType type>
464void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) {
466
467 const CagedHeapLocalData& local_data = CagedHeapLocalData::Get();
468 const AgeTable& age_table = local_data.age_table;
469
470 // Bail out if the slot (precise or imprecise) is in young generation.
471 if (age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung)
472 [[likely]] {
473 return;
474 }
475
476 // Dispatch between different types of barriers.
477 // TODO(chromium:1029379): Consider reload local_data in the slow path to
478 // reduce register pressure.
479 if constexpr (type == GenerationalBarrierType::kPreciseSlot) {
480 GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset,
481 params.heap);
482 } else if constexpr (type ==
484 GenerationalBarrierForUncompressedSlotSlow(
485 local_data, age_table, slot, params.value_offset, params.heap);
486 } else {
487 GenerationalBarrierForSourceObjectSlow(local_data, slot, params.heap);
488 }
489}
490
491#endif // !CPPGC_YOUNG_GENERATION
492
493} // namespace internal
494} // namespace cppgc
495
496#endif // INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_
Definition: heap-handle.h:21
Definition: atomic-entry-flag.h:29
Definition: base-page-handle.h:18
HeapHandle & heap_handle()
Definition: base-page-handle.h:28
static BasePageHandle * FromPayload(void *payload)
Definition: base-page-handle.h:20
Definition: member-storage.h:228
const void * Load() const
Definition: member-storage.h:242
static WriteBarrier::Type Get(const void *slot, RawPointer value, WriteBarrier::Params &params, HeapHandleCallback callback)
Definition: write-barrier.h:326
static WriteBarrier::Type Get(const void *slot, const void *value, WriteBarrier::Params &params, HeapHandleCallback callback)
Definition: write-barrier.h:319
static WriteBarrier::Type Get(const void *value, WriteBarrier::Params &params, HeapHandleCallback callback)
Definition: write-barrier.h:334
Definition: write-barrier.h:39
GenerationalBarrierType
Definition: write-barrier.h:47
static void CheckParams(Type expected_type, const Params &params)
Definition: write-barrier.h:112
Type
Definition: write-barrier.h:41
static Type GetWriteBarrierType(const void *slot, const void *value, Params &params)
Definition: write-barrier.h:395
static void DijkstraMarkingBarrierRange(const Params &params, const void *first_element, size_t element_size, size_t number_of_elements, TraceCallback trace_callback)
Definition: write-barrier.h:438
static void GenerationalBarrier(const Params &params, const void *slot)
Definition: write-barrier.h:105
static bool IsEnabled()
Definition: write-barrier.h:118
static void SteeleMarkingBarrier(const Params &params, const void *object)
Definition: write-barrier.h:449
static void DijkstraMarkingBarrier(const Params &params, const void *object)
Definition: write-barrier.h:426
ValueMode
Definition: write-barrier.h:64
WriteBarrier::Type SetAndReturnType(WriteBarrier::Params &params)
Definition: write-barrier.h:159
Definition: allocation.h:38
void(*)(Visitor *visitor, const void *object) TraceCallback
Definition: trace-trait.h:37
constexpr internal::SentinelPointer kSentinelPointer
Definition: sentinel-pointer.h:35
static WriteBarrier::Type Get(const void *, const void *, WriteBarrier::Params &params, HeapHandleCallback callback)
Definition: write-barrier.h:380
static WriteBarrier::Type Get(const void *, const void *object, WriteBarrier::Params &params, HeapHandleCallback callback)
Definition: write-barrier.h:353
Definition: write-barrier.h:53
HeapHandle * heap
Definition: write-barrier.h:54
#define V8_EXPORT
Definition: v8config.h:867
#define V8_INLINE
Definition: v8config.h:511
#define V8_NOINLINE
Definition: v8config.h:596
#define V8_PRESERVE_MOST
Definition: v8config.h:608