Memory Model

BearVM exposes two heap allocation strategies directly in the IR: manual allocation and arena allocation.

Manual Allocation

alloc allocates memory on the heap. The argument is either a byte size or a struct type name:

%buf = alloc 16
%p   = alloc Person

Allocated memory is zero-initialised. The caller is responsible for freeing it with free:

free %buf
free %p

Failing to call free leaks the memory. Using a pointer after freeing it is undefined behaviour.

Working with Heap-Allocated Structs

To write to a heap-allocated struct, get a reference to the field with get_field_ref, then use store:

struct Person {
    age: int
    score: int
}

@main {
    %p        = alloc Person
    %age_ref  = get_field_ref %p, age
    store %age_ref, 42
    %age_val  = load %age_ref
    call puts(%age_val)
    free %p
    ret 0
}

A struct can also be initialised in one step using a struct literal with store:

%p_ptr = alloc Person
store %p_ptr, { name: "Alice", age: 25 }

Working with Arrays

Arrays are allocated with alloc_array. Elements are accessed by index using get_index_ref:

%arr = alloc_array int, 5

%i = const 0
; write: store each index with its own value
%elem_ptr = get_index_ref %arr, %i
store %elem_ptr, %i

; read
%val = load %elem_ptr

free %arr

Accessing an index outside the allocated length raises IndexOutOfBounds.

Arena Allocation

An arena groups allocations together so they can all be freed in one operation. This is useful when a set of allocations share the same lifetime.

Create an arena with arena_create, allocate from it with arena_alloc, and release everything at once with arena_destroy:

@test_arena {
    %arena = arena_create

    %a = arena_alloc %arena, 64
    %b = arena_alloc %arena, 128
    %c = arena_alloc %arena, 32

    arena_destroy %arena
    ret 0
}

Individual arena allocations cannot be freed. All memory belonging to an arena is released when arena_destroy is called.

Using an invalid arena ID raises InvalidArena.

Choosing Between Manual and Arena Allocation

Use manual allocation when individual objects have independent lifetimes and need to be freed separately.

Use arena allocation when a group of objects all become unreachable at the same point. A single arena_destroy is cheaper and less error-prone than calling free on each individual allocation.