How WebAssembly Supports 64-Bit Memory Addressing

WebAssembly (Wasm) historically limited applications to a 32-bit linear memory space, restricting maximum memory usage to 4 gigabytes. To overcome this limitation for memory-intensive applications, the WebAssembly community developed the “Memory64” proposal, which introduces support for 64-bit memory addressing. This article explains how WebAssembly implements 64-bit memory addressing, the technical changes it introduces to the Wasm instruction set, and the practical implications for developers.

The 32-Bit Limitation

In standard WebAssembly (often referred to as wasm32), the linear memory is indexed using 32-bit unsigned integers. This means that instructions like loads, stores, and memory management operations (such as growing the memory) use i32 values to represent addresses. Consequently, the maximum addressable space is \(2^{32}\) bytes, which translates to exactly 4 GiB of virtual memory.

While 4 GiB is sufficient for typical web applications, it acts as a bottleneck for complex desktop-class software ported to the web, such as large-scale databases, video editing suites, scientific simulations, and modern game engines.

Introducing Memory64

The Memory64 proposal (wasm64) solves this bottleneck by allowing WebAssembly linear memory to be indexed using 64-bit integers (i64). This expands the theoretical maximum addressable memory space to \(2^{64}\) bytes (16 exabytes), though actual execution environments and operating systems will impose much lower, practical runtime limits.

To support 64-bit addressing, WebAssembly introduces changes to several core components of its specification:

1. Memory Definitions

When defining a memory module in Wasm, the developer or compiler can now specify the index type. A memory definition includes a flag indicating whether it uses i32 (wasm32) or i64 (wasm64) for indexing.

2. Instruction Set Adaptation

All memory-related instructions are adapted to use the 64-bit index type when operating on a 64-bit memory. This includes: * Loads and Stores: Instructions like i32.load, i64.store, and their variants accept 64-bit effective addresses (composed of a 64-bit base address from the stack and a static offset). * Memory Management: The memory.size and memory.grow instructions return and accept i64 values, respectively, allowing the engine to allocate and report memory sizes beyond the 4 GiB barrier. * Bulk Memory Operations: Operations like memory.copy, memory.fill, and memory.init use 64-bit integers for offsets and element counts.

3. Table Offsets

Similar to linear memory, WebAssembly tables can also benefit from 64-bit indexing. Under the Memory64 proposal, table instructions (like table.get, table.set, and table.grow) can use i64 types to index elements, allowing for much larger tables of function references or objects.

Practical Considerations and Support

To use 64-bit memory addressing, developers must compile their source code (usually written in C, C++, Rust, or Zig) targeting the wasm64 architecture instead of the traditional wasm32 target. Toolchains like Emscripten and LLVM provide built-in flags to enable this target.

At the runtime level, major web browsers and standalone WebAssembly runtimes (such as Wasmtime and Wasmer) support the Memory64 proposal, either natively or behind experimental flags. Because 64-bit addressing requires 64-bit pointers, applications running on wasm64 may experience a slight increase in memory overhead (known as pointer bloat) and minor cache performance differences compared to wasm32, but they gain the ability to process massive datasets seamlessly.