Appendix A: A Simple Allocator
bump-allocator

Write a bump allocator:

var heap_start: i64 = 500000;
var heap_ptr: i64 = 500000;
var heap_limit: i64 = 900000;

fn alloc(size: i64) i64 {
    var ptr: i64 = heap_ptr;
    heap_ptr += (size + 7) / 8 * 8;
    if (heap_ptr > heap_limit) { return 0; }
    return ptr;
}

That's it. alloc(100) returns 500000 and advances heap_ptr to 500104 (rounded up to a multiple of 8 for alignment). The next alloc(50) returns 500104 and advances to 500160. Each call gets a fresh, non-overlapping region.

The (size + 7) / 8 * 8 rounds up to 8-byte alignment. WAT's i64.load and i64.store require aligned addresses. Without rounding, alloc(3) followed by alloc(8) would place an i64 at a misaligned address.

Test:

    check(
        \\var heap_ptr: i64 = 500000;
        \\var heap_limit: i64 = 900000;
        \\fn alloc(size: i64) i64 {
        \\    var ptr: i64 = heap_ptr;
        \\    heap_ptr += (size + 7) / 8 * 8;
        \\    if (heap_ptr > heap_limit) { return 0; }
        \\    return ptr;
        \\}
        \\var a: i64 = alloc(100);
        \\var b: i64 = alloc(200);
        \\b - a
    , 104);  // 100 rounded up to 104 (next multiple of 8)

The bump allocator is fast (one addition per call), simple (six lines), and useful for programs that only allocate and never free -- compilers, for instance. Our self-hosted compiler could use this instead of pre-assigned addresses.

But it can't free. Memory only grows. For long-running programs, we need something better.

### Block headers

To free memory, we need to know how big each allocation was. We store this information in a header -- 8 bytes immediately before the returned pointer.

  Header (8 bytes)     Payload (size bytes)
  ┌────────────────┬────────────────────────────┐
  │  block_size    │  usable memory             │
  └────────────────┴────────────────────────────┘
  ^                ^
  block address    pointer returned by alloc()

alloc(size) allocates size + 8 bytes, writes the total block size into the header, and returns a pointer to the payload. free(ptr) reads the header at ptr - 8 to learn the block size.