Tagged Pointers in V8 are a memory optimization technique where the least significant bit of a pointer is used to distinguish between small integers (SMIs) and actual pointers to heap objects, enabling efficient type checking without extra memory lookups.
Tagged pointers are a fundamental implementation technique in V8 where every value (JSValue) is represented as a tagged machine word. Instead of storing type information separately, V8 encodes the type directly into the pointer value by using the lowest bits as tags. This allows the engine to quickly determine whether a value is a small integer (SMI) or a reference to a heap object simply by checking the last bit, without additional memory accesses . This design leverages the fact that pointers in modern systems are aligned to word boundaries (typically 8 bytes), meaning the lowest few bits are always zero and can be repurposed as tags .
Tag Scheme: V8 uses the least significant bit (LSB) for tagging: 0 indicates a Small Integer (SMI), and 1 indicates a pointer to a heap object (HeapObject) .
SMI Encoding: Small integers are stored as shifted values. In 64-bit systems, the integer is left-shifted by 32 bits, preserving the LSB as 0. This means the actual integer value is (tagged_value >> 32) .
Pointer Encoding: Heap object pointers have their LSB set to 1. To get the actual memory address, the tagged value must be decremented by 1 (pointer = tagged_value & ~1) .
Alignment Requirement: This technique works because heap objects are allocated at addresses that are multiples of 2, ensuring the LSB is naturally 0 and can be replaced with a tag .
The Tagged Pointer scheme is implemented through V8's TaggedImpl class, which provides a unified interface for handling both full pointers and compressed pointers . This abstraction allows V8 code to work with different pointer representations without specialized handling at every usage site. The StorageType template parameter can be either Address (full pointer) or Tagged_t (compressed pointer), enabling support for pointer compression where heap references are stored as 32-bit offsets from a base address to save memory .
Type Checking Efficiency: Type determination becomes a single bit test instead of a memory lookup, significantly speeding up operations .
Memory Savings: No separate type tags or headers needed for primitive values—small integers are stored directly in the value slot .
Cache Friendliness: More values fit in CPU caches because SMIs don't require heap allocation or indirection .
Pointer Compression Integration: Tagged pointers work seamlessly with V8's pointer compression, where the tag occupies the lowest bits of a 32-bit offset .
Debugging Visibility: In heap snapshots, all object addresses appear as odd numbers (LSB = 1), confirming they are tagged pointers .
The SMI range is platform-dependent: in 32-bit systems, SMIs are 31-bit signed integers (-1073741823 to 1073741823), while 64-bit systems typically use 32-bit signed integers (-2147483647 to 2147483647) . V8 also provides a V8_31BIT_SMIS_ON_64BIT_ARCH flag for cross-platform consistency when needed. This tagging system is fundamental to V8's performance, enabling operations like addition on SMIs to be performed with a single CPU instruction after shifting, while pointer operations simply require masking out the tag bit .