docs/heap/pointer-tagging.md
V8 uses pointer tagging to efficiently represent different types of values in a single word (pointer-sized). This avoids the overhead of separate type tags for every value, which is especially important for small integers (Smis) and heap object references.
V8 uses the least significant bits of a pointer-sized word to encode the type of the value.
Small integers are encoded directly in the pointer word.
0.The exact representation of Smis depends on the architecture and whether pointer compression is enabled:
[31 bits value][0].[32 bits value][32 bits zeros].References to objects on the heap are tagged with a non-zero value in the least significant bits.
01.11.
kHeapObjectTag = 1kWeakHeapObjectTag = 3kHeapObjectTagMask = 3When pointer compression is enabled (the default on 64-bit platforms), pointers are stored as 32-bit values. These compressed pointers are relative to the isolate's cage base address (which is often stored in a dedicated register, e.g., r13 on x64). To decode a compressed pointer to a full 64-bit address, V8 adds it to the cage base.
Compressed pointers are represented in C++ with the Tagged_t type. Tagged_t is uint32_t when pointer compression is enabled, and Address (system-pointer-sized) otherwise.
During garbage collection, V8 uses the least significant bits 00 in the object header (MapWord) to indicate a forwarding pointer to the new location of the object. While this looks like a Smi (LSB is 0), it is only valid in the context of the object header during GC.
Tagged<T>V8 C++ code uses the Tagged<T> template class (defined in src/objects/tagged.h) to represent tagged pointers. This provides type safety and encapsulates the tagging operations.
Tagged<Object>: Can represent any tagged value (Smi or HeapObject).Tagged<Smi>: Represents a small integer.Tagged<HeapObject>: Represents a pointer to any object on the heap.Tagged<MyType>: Represents a pointer to a specific heap object type (e.g., Tagged<JSArray>).The Tagged class handles untagging automatically when dereferencing (e.g., via operator->) or when accessing the raw address via address().
To get the actual memory address of a heap object from a tagged pointer, the tag must be subtracted:
Address address() const { return this->ptr() - kHeapObjectTag; }
Conversely, to create a tagged pointer from an address, the tag is added:
Tagged(reinterpret_cast<Address>(ptr) + kHeapObjectTag)
For weak pointers, the weak bit is bitwise-ORed:
return Tagged<WeakOf<T>>(value.ptr() | kWeakHeapObjectTag);