raw
current commit
261004.7 · deps: update V8 to 15.2.124.38

Pointer Tagging in V8

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.

The Tagging Scheme

V8 uses the least significant bits of a pointer-sized word to encode the type of the value.

Small Integers (Smi)

Small integers are encoded directly in the pointer word.

Smi Representation Details

The exact representation of Smis depends on the architecture and whether pointer compression is enabled:

Heap Objects

References to objects on the heap are tagged with a non-zero value in the least significant bits.

Compressed Pointers

When 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.

Forwarding Pointers (GC)

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.

C++ Representation: 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.

The Tagged class handles untagging automatically when dereferencing (e.g., via operator->) or when accessing the raw address via address().

Address Calculation

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);

See Also