Intel LAM and ARM TBI: Hardware Pointer Tagging Without Masking on Every Dereference

2026-08-24

On 64-bit systems, virtual addresses are 48 or 57 bits, but pointers are 64 bits. The top 16 bits are wasted — required to be a sign extension of bit 47 (or 56), and the CPU faults if they aren't. Language runtimes have wanted those bits for decades: to store type tags, generation counts, NaN-boxed values, or lock states without an extra memory access. The workaround was software masking: every dereference did ptr & 0x0000ffffffffffff before the load.

ARM's Top Byte Ignore (TBI), shipped in ARMv8 in 2011, made the top 8 bits of a virtual address silently ignored by the MMU on data accesses. No mask needed. Intel's Linear Address Masking (LAM), introduced on Sapphire Rapids and enabled in Linux 6.4 (2023), does the same for the top 6 bits (LAM_U57) or top 15 bits (LAM_U48).

You opt in per-process via arch_prctl(ARCH_ENABLE_TAGGED_ADDR) on Linux. Once enabled, the CPU strips your tag bits before translation on loads, stores, and instruction fetch. The tag rides along in the register — do pointer arithmetic and it survives.

Concrete win: HWASan (Hardware-assisted AddressSanitizer) on ARM tags every 16-byte allocation granule with a 4-bit color stored in the top byte of the pointer and in shadow memory. On every load, the CPU dereferences using the low 56 bits; a separate check compares the pointer's tag byte with the shadow tag. Mismatch → SIGSEGV. Overhead is ~15% versus ASan's 2–3×, because the mask step is free. Google Chrome and Android userspace ship this in production.

V8's compressed pointers and OpenJDK's Shenandoah GC use the top bits for forwarding pointers during concurrent compaction — no read barriers on the mutator's fast path.

Rule of thumb: If your language runtime already does a mask on every pointer dereference, LAM/TBI eliminates ~1 instruction and ~1 cycle per load. In a pointer-chasing workload doing 500M loads/sec/core, that's a 3–8% throughput win, entirely free.

Gotchas: tagged pointers passed to the kernel (syscalls, ioctls) get rejected unless the syscall is on the ABI's tagged-pointer allowlist — the kernel doesn't want to guess whether your 0xff00... is a tag or a canonical kernel address. And mmap() returns untagged pointers; you tag them yourself. Instruction fetch on Intel LAM is not masked — you cannot tag function pointers.

Key Takeaway: LAM and TBI let userspace stash metadata in the top bits of pointers and dereference them directly, turning a decades-old software mask into a zero-cost hardware feature that memory-safety tools and GCs already ship in production.

All newsletters