1// Copyright 2018 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_HEAP_WRITE_BARRIER_INL_H_
6#define V8_HEAP_HEAP_WRITE_BARRIER_INL_H_
7
8// Clients of this interface shouldn't depend on lots of heap internals.
9// Do not include anything from src/heap here!
10
11#include "src/heap/heap-write-barrier.h"
12
13#include "src/common/globals.h"
14#include "src/objects/code.h"
15#include "src/objects/compressed-slots-inl.h"
16#include "src/objects/fixed-array.h"
17#include "src/objects/heap-object.h"
18#include "src/objects/maybe-object-inl.h"
19#include "src/objects/slots-inl.h"
20
21namespace v8 {
22namespace internal {
23
24// Defined in heap.cc.
25V8_EXPORT_PRIVATE bool Heap_PageFlagsAreConsistent(HeapObject object);
26V8_EXPORT_PRIVATE bool Heap_ValueMightRequireGenerationalWriteBarrier(
27    HeapObject value);
28V8_EXPORT_PRIVATE void Heap_GenerationalBarrierSlow(HeapObject object,
29                                                    Address slot,
30                                                    HeapObject value);
31V8_EXPORT_PRIVATE void Heap_WriteBarrierForCodeSlow(Code host);
32
33V8_EXPORT_PRIVATE void Heap_GenerationalBarrierForCodeSlow(Code host,
34                                                           RelocInfo* rinfo,
35                                                           HeapObject object);
36
37V8_EXPORT_PRIVATE void Heap_GenerationalEphemeronKeyBarrierSlow(
38    Heap* heap, EphemeronHashTable table, Address slot);
39
40// Do not use these internal details anywhere outside of this file. These
41// internals are only intended to shortcut write barrier checks.
42namespace heap_internals {
43
44struct MemoryChunk {
45  static constexpr uintptr_t kFlagsOffset = kSizetSize;
46  static constexpr uintptr_t kHeapOffset = kSizetSize + kUIntptrSize;
47  static constexpr uintptr_t kIsExecutableBit = uintptr_t{1} << 0;
48  static constexpr uintptr_t kMarkingBit = uintptr_t{1} << 17;
49  static constexpr uintptr_t kFromPageBit = uintptr_t{1} << 3;
50  static constexpr uintptr_t kToPageBit = uintptr_t{1} << 4;
51  static constexpr uintptr_t kReadOnlySpaceBit = uintptr_t{1} << 20;
52
53  V8_INLINE static heap_internals::MemoryChunk* FromHeapObject(
54      HeapObject object) {
55    DCHECK(!V8_ENABLE_THIRD_PARTY_HEAP_BOOL);
56    return reinterpret_cast<MemoryChunk*>(object.ptr() & ~kPageAlignmentMask);
57  }
58
59  V8_INLINE bool IsMarking() const { return GetFlags() & kMarkingBit; }
60
61  V8_INLINE bool InYoungGeneration() const {
62    if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return false;
63    constexpr uintptr_t kYoungGenerationMask = kFromPageBit | kToPageBit;
64    return GetFlags() & kYoungGenerationMask;
65  }
66
67  V8_INLINE uintptr_t GetFlags() const {
68    return *reinterpret_cast<const uintptr_t*>(reinterpret_cast<Address>(this) +
69                                               kFlagsOffset);
70  }
71
72  V8_INLINE Heap* GetHeap() {
73    Heap* heap = *reinterpret_cast<Heap**>(reinterpret_cast<Address>(this) +
74                                           kHeapOffset);
75    DCHECK_NOT_NULL(heap);
76    return heap;
77  }
78
79  V8_INLINE bool InReadOnlySpace() const {
80    return GetFlags() & kReadOnlySpaceBit;
81  }
82
83  V8_INLINE bool InCodeSpace() const { return GetFlags() & kIsExecutableBit; }
84};
85
86inline void GenerationalBarrierInternal(HeapObject object, Address slot,
87                                        HeapObject value) {
88  DCHECK(Heap_PageFlagsAreConsistent(object));
89  heap_internals::MemoryChunk* value_chunk =
90      heap_internals::MemoryChunk::FromHeapObject(value);
91  heap_internals::MemoryChunk* object_chunk =
92      heap_internals::MemoryChunk::FromHeapObject(object);
93
94  if (!value_chunk->InYoungGeneration() || object_chunk->InYoungGeneration()) {
95    return;
96  }
97
98  Heap_GenerationalBarrierSlow(object, slot, value);
99}
100
101inline void GenerationalEphemeronKeyBarrierInternal(EphemeronHashTable table,
102                                                    Address slot,
103                                                    HeapObject value) {
104  DCHECK(Heap::PageFlagsAreConsistent(table));
105  heap_internals::MemoryChunk* value_chunk =
106      heap_internals::MemoryChunk::FromHeapObject(value);
107  heap_internals::MemoryChunk* table_chunk =
108      heap_internals::MemoryChunk::FromHeapObject(table);
109
110  if (!value_chunk->InYoungGeneration() || table_chunk->InYoungGeneration()) {
111    return;
112  }
113
114  Heap_GenerationalEphemeronKeyBarrierSlow(table_chunk->GetHeap(), table, slot);
115}
116
117}  // namespace heap_internals
118
119inline void WriteBarrierForCode(Code host, RelocInfo* rinfo, Object value) {
120  DCHECK(!HasWeakHeapObjectTag(value));
121  if (!value.IsHeapObject()) return;
122  WriteBarrierForCode(host, rinfo, HeapObject::cast(value));
123}
124
125inline void WriteBarrierForCode(Code host, RelocInfo* rinfo, HeapObject value) {
126  GenerationalBarrierForCode(host, rinfo, value);
127  WriteBarrier::Marking(host, rinfo, value);
128}
129
130inline void WriteBarrierForCode(Code host) {
131  Heap_WriteBarrierForCodeSlow(host);
132}
133
134inline void GenerationalBarrier(HeapObject object, ObjectSlot slot,
135                                Object value) {
136  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
137  DCHECK(!HasWeakHeapObjectTag(value));
138  if (!value.IsHeapObject()) return;
139  GenerationalBarrier(object, slot, HeapObject::cast(value));
140}
141
142inline void GenerationalBarrier(HeapObject object, ObjectSlot slot,
143                                Code value) {
144  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
145  DCHECK(!Heap_ValueMightRequireGenerationalWriteBarrier(value));
146}
147
148inline void GenerationalBarrier(HeapObject object, ObjectSlot slot,
149                                HeapObject value) {
150  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
151  DCHECK(!HasWeakHeapObjectTag(*slot));
152  heap_internals::GenerationalBarrierInternal(object, slot.address(), value);
153}
154
155inline void GenerationalEphemeronKeyBarrier(EphemeronHashTable table,
156                                            ObjectSlot slot, Object value) {
157  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
158  DCHECK(!HasWeakHeapObjectTag(*slot));
159  DCHECK(!HasWeakHeapObjectTag(value));
160  DCHECK(value.IsHeapObject());
161  heap_internals::GenerationalEphemeronKeyBarrierInternal(
162      table, slot.address(), HeapObject::cast(value));
163}
164
165inline void GenerationalBarrier(HeapObject object, MaybeObjectSlot slot,
166                                MaybeObject value) {
167  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
168  HeapObject value_heap_object;
169  if (!value->GetHeapObject(&value_heap_object)) return;
170  heap_internals::GenerationalBarrierInternal(object, slot.address(),
171                                              value_heap_object);
172}
173
174inline void GenerationalBarrierForCode(Code host, RelocInfo* rinfo,
175                                       HeapObject object) {
176  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
177  heap_internals::MemoryChunk* object_chunk =
178      heap_internals::MemoryChunk::FromHeapObject(object);
179  if (!object_chunk->InYoungGeneration()) return;
180  Heap_GenerationalBarrierForCodeSlow(host, rinfo, object);
181}
182
183inline WriteBarrierMode GetWriteBarrierModeForObject(
184    HeapObject object, const DisallowGarbageCollection* promise) {
185  if (FLAG_disable_write_barriers) return SKIP_WRITE_BARRIER;
186  DCHECK(Heap_PageFlagsAreConsistent(object));
187  heap_internals::MemoryChunk* chunk =
188      heap_internals::MemoryChunk::FromHeapObject(object);
189  if (chunk->IsMarking()) return UPDATE_WRITE_BARRIER;
190  if (chunk->InYoungGeneration()) return SKIP_WRITE_BARRIER;
191  return UPDATE_WRITE_BARRIER;
192}
193
194inline bool ObjectInYoungGeneration(Object object) {
195  // TODO(rong): Fix caller of this function when we deploy
196  // v8_use_third_party_heap.
197  if (FLAG_single_generation) return false;
198  if (object.IsSmi()) return false;
199  return heap_internals::MemoryChunk::FromHeapObject(HeapObject::cast(object))
200      ->InYoungGeneration();
201}
202
203inline bool IsReadOnlyHeapObject(HeapObject object) {
204  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return ReadOnlyHeap::Contains(object);
205  heap_internals::MemoryChunk* chunk =
206      heap_internals::MemoryChunk::FromHeapObject(object);
207  return chunk->InReadOnlySpace();
208}
209
210inline bool IsCodeSpaceObject(HeapObject object) {
211  heap_internals::MemoryChunk* chunk =
212      heap_internals::MemoryChunk::FromHeapObject(object);
213  return chunk->InCodeSpace();
214}
215
216base::Optional<Heap*> WriteBarrier::GetHeapIfMarking(HeapObject object) {
217  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return {};
218  heap_internals::MemoryChunk* chunk =
219      heap_internals::MemoryChunk::FromHeapObject(object);
220  if (!chunk->IsMarking()) return {};
221  return chunk->GetHeap();
222}
223
224void WriteBarrier::Marking(HeapObject host, ObjectSlot slot, Object value) {
225  DCHECK(!HasWeakHeapObjectTag(value));
226  if (!value.IsHeapObject()) return;
227  Marking(host, HeapObjectSlot(slot), HeapObject::cast(value));
228}
229
230void WriteBarrier::Marking(HeapObject host, MaybeObjectSlot slot,
231                           MaybeObject value) {
232  HeapObject value_heap_object;
233  if (!value->GetHeapObject(&value_heap_object)) return;
234  Marking(host, HeapObjectSlot(slot), value_heap_object);
235}
236
237void WriteBarrier::Marking(HeapObject host, HeapObjectSlot slot,
238                           HeapObject value) {
239  auto heap = GetHeapIfMarking(host);
240  if (!heap) return;
241  MarkingSlow(*heap, host, slot, value);
242}
243
244void WriteBarrier::Marking(Code host, RelocInfo* reloc_info, HeapObject value) {
245  auto heap = GetHeapIfMarking(host);
246  if (!heap) return;
247  MarkingSlow(*heap, host, reloc_info, value);
248}
249
250void WriteBarrier::Marking(JSArrayBuffer host,
251                           ArrayBufferExtension* extension) {
252  if (!extension) return;
253  auto heap = GetHeapIfMarking(host);
254  if (!heap) return;
255  MarkingSlow(*heap, host, extension);
256}
257
258void WriteBarrier::Marking(DescriptorArray descriptor_array,
259                           int number_of_own_descriptors) {
260  auto heap = GetHeapIfMarking(descriptor_array);
261  if (!heap) return;
262  MarkingSlow(*heap, descriptor_array, number_of_own_descriptors);
263}
264
265// static
266void WriteBarrier::MarkingFromGlobalHandle(Object value) {
267  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
268  if (!value.IsHeapObject()) return;
269
270  HeapObject heap_value = HeapObject::cast(value);
271  // Value may be in read only space but the chunk should never be marked
272  // as marking which would result in a bail out.
273  auto heap = GetHeapIfMarking(heap_value);
274  if (!heap) return;
275  MarkingSlowFromGlobalHandle(*heap, heap_value);
276}
277
278// static
279void WriteBarrier::MarkingFromInternalFields(JSObject host) {
280  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
281  auto heap = GetHeapIfMarking(host);
282  if (!heap) return;
283  MarkingSlowFromInternalFields(*heap, host);
284}
285
286#ifdef ENABLE_SLOW_DCHECKS
287// static
288template <typename T>
289bool WriteBarrier::IsRequired(HeapObject host, T value) {
290  if (BasicMemoryChunk::FromHeapObject(host)->InYoungGeneration()) return false;
291  if (value.IsSmi()) return false;
292  if (value.IsCleared()) return false;
293  HeapObject target = value.GetHeapObject();
294  if (ReadOnlyHeap::Contains(target)) return false;
295  return !IsImmortalImmovableHeapObject(target);
296}
297#endif
298
299}  // namespace internal
300}  // namespace v8
301
302#endif  // V8_HEAP_HEAP_WRITE_BARRIER_INL_H_
303