![]() |
v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
|
V8 uses a write barrier to inform the GC about changes to the heap by the mutator. A write barrier is emitted for heap stores like host.field = value
. The write barrier is required for multiple purposes:
The generational barrier is always enabled, while the other barriers are only enabled while incremental/concurrent marking is running.
The barrier is split into multiple separate parts (ordered from fastest to slowest):
The fast and deferred code paths of the write barrier have different implementations in all compilers and C++. These code paths are also emitted for each write barrier/call site. Only the slow path is shared across all write barrier locations.
CSA and the interpreter do not have their own barrier but reuse Turbofan's write barrier. While implementations for compilers differ, the general idea is still pretty much the same for all of them.
Checks whether the host object has the POINTERS_FROM_HERE_ARE_INTERESTING
flag set on its page header. If yes, the write barrier will jump into the deferred code path. Otherwise regular execution continues as usual. This check will be skipped if value is a Smi.
kArchAtomicStoreWithWriteBarrier
instruction opcode.Checks whether the value object has the POINTERS_TO_HERE_ARE_INTERESTING
flag set on its page header. If yes, the write barrier will call the builtin function for the slow path. Otherwise the deferred code jumps back to the regular instruction stream.
The slow path of the barrier is shared between all compilers and call sites. In the generational barrier the field/slot is simply recorded in the old-to-new slot set. During incremental/concurrent marking it also marks the value
object and may record the slot in the old-to-old remembered set for pointers to evacuation candidates. This part of the barrier is implemented in CSA in builtins-internal-gen.cc and used in the RecordWriteXXX builtins.
Inserting into the slot set in the RecordWriteXXX builtins may need to malloc()
memory. The builtin therefore may call into C++ for this operation. The C++ function for this is Heap::InsertIntoRememberedSetFromCode.
V8 also needs a special barrier for pointers into the trusted space with the sandbox. This barrier is only implemented for 64-bit architectures and only used in builtins written in either CSA or assembler.
This barrier is only active during incremental marking. Therefore the fast path of this barrier only jumps into the deferred code path if marking is active. Checking the host object flags is not necessary.
The Turbofan indirect pointer write barrier is implemented as a separate instruction opcode kArchStoreIndirectWithWriteBarrier. The deferred code path is implemented in the OutOfLineRecordWrite class along with the regular write barrier.
Assembler builtins can emit this barrier using MacroAssembler::RecordWrite. This method handles both the indirect pointer but also the regular write barrier.