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.