Linear Memory

8.1 Memory Basics

Linear memory is a byte array measured in pages of 64 KiB. It has an initial and an optional maximum size, and can grow (memory.grow) but never shrink.

Wasm32 addresses are i32 (max 4 GiB). Memory64 allows i64 addresses (Module 27).

All accesses are bounds-checked. Out-of-bounds accesses trap.

(module
  (memory (export "memory") 1 10)             ;; 1 page initially, max 10 pages
  (data (i32.const 0) "Hello, Wasm!")         ;; active data segment at offset 0

  (func (export "sum_bytes") (param $ptr i32) (param $len i32) (result i32)
    (local $i i32) (local $acc i32)
    (block $done
      (loop $next
        (br_if $done (i32.ge_u (local.get $i) (local.get $len)))
        (local.set $acc
          (i32.add (local.get $acc)
                   (i32.load8_u (i32.add (local.get $ptr) (local.get $i)))))
        (local.set $i (i32.add (local.get $i) (i32.const 1)))
        (br $next)))
    (local.get $acc))

  (func (export "grow") (param $pages i32) (result i32)
    (memory.grow (local.get $pages)))            ;; returns the previous size in pages, or -1 on failure

  (func (export "size") (result i32) (memory.size)))

8.2 Load and Store Instructions

i32.load, i64.load, f32.load, f64.load Load full-width values

i32.load8_s/u, i32.load16_s/u, i64.load8/16/32_s/u Load narrower values with sign/zero extension

i32.store, i64.store, f32.store, f64.store i32.store8, i32.store16, i64.store8/16/32 Store narrower values memory.size, memory.grow

memory.fill, memory.copy, memory.init, data.drop Bulk memory (Module 21)

offset is a static immediate added to the address. align is a hint (a power of two). Wasm is little-endian.

(i32.store offset=4 align=4 (local.get $ptr) (i32.const 99))   ;; store at ptr+4
(i32.load offset=8 (local.get $ptr))

8.3 Memory Management Strategies

Wasm itself has no malloc. Languages bring an allocator in linear memory:

C/C++: dlmalloc/emmalloc/mimalloc (Emscripten), wasi-libc malloc

Rust: its default allocator (dlmalloc for wasm32), or talc/lol_alloc for size

AssemblyScript: incremental/minimal/stub runtimes A simple bump allocator in WAT:

Memory layout in typical compiled programs: [static data | stack (grows down, in linear memory for address-taken locals) | heap (grows up)].

(global $heap (mut i32) (i32.const 1024))
(func $alloc (export "alloc") (param $size i32) (result i32)
  (local $ptr i32)
  (local.set $ptr (global.get $heap))
  (global.set $heap (i32.add (global.get $heap) (local.get $size)))
  (local.get $ptr))