WebAssembly中load与store的使用及内存管理方法问询
load and store for WebAssembly Memory in WAT Great question! It’s true that most Wasm examples rely on C/C++ compiles, so diving into raw WAT for memory operations is a fantastic way to grasp how Wasm works under the hood. Let’s break this down step by step with practical, hands-on examples.
First: Declare and Export Memory
Before you can read or write memory, you need to define a memory instance in your WAT module. Wasm memory is split into "pages" (each 64KB), and you’ll need to set an initial size (required) and can optionally set a maximum size. Exporting the memory lets external code (like JavaScript) interact with it too.
Here’s how to add memory to your module:
(module ; Define memory: 1 initial page (64KB), no max limit specified (memory 1) ; Export the memory so external code can access it (export "memory" (memory 0)) )
Using store to Write to Memory
The store family of instructions writes values from the Wasm stack into a specific memory address. For 32-bit integers, we use i32.store—it pulls two values from the stack: first the target memory address (byte offset), then the value to store.
Let’s add a function that stores an i32 at a user-specified address:
(func (export "store_i32") (param $addr i32) (param $value i32) get_local $addr ; Push the target address onto the stack get_local $value ; Push the value we want to store onto the stack i32.store ; Write the value to the address (stack pops both values) )
Key Notes for store:
- Memory addresses are byte offsets. Since an
i32takes 4 bytes, consecutivei32values should be stored at addresses spaced by 4 (e.g., 0, 4, 8) to avoid overlapping data. - For smaller data types, use specialized instructions:
i32.store8: Write only the lowest 8 bits of ani32to a single byte (no 4-byte alignment required)i32.store16: Write the lowest 16 bits to two bytes
Using load to Read from Memory
The load family does the opposite: it reads a value from a memory address and pushes it onto the Wasm stack. For i32 values, use i32.load.
Here’s a function to read an i32 from a specified address:
(func (export "load_i32") (param $addr i32) (result i32) get_local $addr ; Push the address we want to read from onto the stack i32.load ; Read 4 bytes from the address, push the resulting i32 to the stack )
Key Notes for load:
- Like
store, specialized instructions exist for smaller types:i32.load8_u: Read 1 byte as an unsigned 32-bit integeri32.load8_s: Read 1 byte as a signed 32-bit integeri32.load16_u/i32.load16_s: Read 2 bytes as unsigned/signed values
- Wasm uses little-endian byte order for multi-byte values (matching x86 systems).
Combining with Global Variables
You can use global variables to track base addresses for your data (so you don’t have to hardcode offsets everywhere). Let’s update our module to include a global base address for organized storage:
(module (memory 1) (export "memory" (memory 0)) ; Global variable: base address for our data (start at 16 to avoid reserved memory) (global $data_base i32 (i32.const 16)) ; Store a value directly at the base address (func (export "store_to_base") (param $value i32) get_global $data_base get_local $value i32.store ) ; Load a value directly from the base address (func (export "load_from_base") (result i32) get_global $data_base i32.load ) )
Full Working Example
Here’s a complete WAT module wrapping all these concepts together. Save it as memory_demo.wat, then compile it with wat2wasm memory_demo.wat -o memory_demo.wasm:
(module (memory 1) (export "memory" (memory 0)) (global $data_base i32 (i32.const 16)) (func (export "store_i32") (param $addr i32) (param $value i32) get_local $addr get_local $value i32.store) (func (export "load_i32") (param $addr i32) (result i32) get_local $addr i32.load) (func (export "store_to_base") (param $value i32) get_global $data_base get_local $value i32.store) (func (export "load_from_base") (result i32) get_global $data_base i32.load) (func (export "store_u8") (param $addr i32) (param $value i32) get_local $addr get_local $value i32.store8) (func (export "load_u8") (param $addr i32) (result i32) get_local $addr i32.load8_u) )
Quick JS Interaction (Bonus)
If you want to test this module in JavaScript, here’s a snippet to call the exported functions and even directly access the memory buffer:
async function runMemoryDemo() { const wasmModule = await WebAssembly.instantiateStreaming(fetch('memory_demo.wasm')); const { memory, store_i32, load_i32, store_to_base, load_from_base } = wasmModule.instance.exports; // Store 42 at address 20, then read it back store_i32(20, 42); console.log(load_i32(20)); // Logs: 42 // Store 123 at the global base address, then read it back store_to_base(123); console.log(load_from_base()); // Logs: 123 // Directly access memory via a Uint32Array view const memoryView = new Uint32Array(memory.buffer); console.log(memoryView[5]); // Address 20 = 5 * 4 bytes, logs: 42 } runMemoryDemo();
内容的提问来源于stack exchange,提问作者Lance Pollard

