What Are SMI Small Integers in JavaScript Engines?

Small Integers, commonly known as SMIs (pronounced “smees”), are a critical internal optimization used by JavaScript engines like Google’s V8, SpiderMonkey, and JavaScriptCore to represent integer values efficiently. Although the ECMAScript specification defines all numbers as standard 64-bit floating-point values (doubles), allocating a full 64-bit float or creating a heap object for simple operations—like loop counters or array indices—causes severe memory overhead and performance degradation. JavaScript engines solve this by using pointer tagging to store integers directly within pointer registers without heap allocation, significantly speeding up execution and reducing memory usage.

The Problem with JavaScript Numbers

According to the ECMAScript specification, the number type is an IEEE 754 standard 64-bit double-precision floating-point value. In traditional implementations, storing complex data requires allocating memory on the heap and passing around memory addresses (pointers).

If a JavaScript engine created a heap-allocated object (HeapNumber) for every single integer calculation, memory allocations and Garbage Collection (GC) pauses would quickly cripple performance.

What Is an SMI?

An SMI is a signed integer that fits within a specific range, typically 31 bits on 32-bit systems and 31 or 32 bits on 64-bit architectures.

Instead of treating integers as full 64-bit floating-point numbers on the heap, engines treat SMIs as immediate, unboxed values. They are stored directly inside the variable slot or CPU register rather than being wrapped in an object or referenced through a memory pointer.

How Pointer Tagging Enables SMIs

Modern computers align memory addresses for heap objects to 4-byte or 8-byte boundaries. Because these addresses are multiples of 4 or 8, their binary representations always end in at least two or three zero bits (e.g., ...00 or ...000).

JavaScript engines take advantage of these unused low-order bits through a technique called pointer tagging:

Representation in Memory

Performance Benefits of SMIs

  1. Zero Heap Allocation: Because SMIs are stored directly in the reference slot, no dynamic memory is allocated on the heap, completely bypassing the garbage collector.
  2. Fast Arithmetic: Basic math operations can be executed with minimal overhead. For instance, addition can often be performed directly on tagged values, or by simply shifting the bits to remove the tag, executing the CPU instruction, and shifting back.
  3. Memory Density: Arrays containing only SMIs (known in V8 as PACKED_SMI_ELEMENTS) are stored as contiguous arrays of unboxed integers, resulting in maximum cache locality and minimal memory footprint.

Transitioning Out of SMI

When an integer exceeds the maximum SMI range (e.g., surpassing \(2^{30} - 1\) on a 31-bit system), or when a fractional/floating-point operation occurs, the engine automatically converts the SMI into a boxed HeapNumber (a double-precision float on the heap) or a 64-bit float representation. This process is transparent to the developer, though keeping numeric values within the SMI range allows JavaScript code to run at peak optimization.