comptime VM: memory = arena of stable host allocations; Addr = real host pointer (Phase 4D.0)

Replace the growable ArrayList(u8) flat buffer (reallocs/MOVES on growth) with a
std.heap.ArenaAllocator. Each allocBytes is a separate arena allocation that never
moves and is freed wholesale on deinit -- no per-object free, no cap, no fixed
buffer. Addr is now the allocation's ABSOLUTE host pointer (@intFromPtr), not an
offset, so a flat-memory pointer and an FFI-returned host pointer are the same kind
of value -- the FFI bridge (4D.1) passes them to/from libc with zero translation and
no per-call pinning (the moving-buffer hazard is gone by construction).

readWord/writeWord/bytes deref the absolute pointer with a null-check bail (the
malformed-IR / null-deref safety contract). Dropped the offset-based upper-bounds
check (can't bound an absolute pointer; Frame.bad_ref still catches the dominant
malformed-IR vector) and the test-only mark/reset (arena has no reset-to-mark; the
VM never used them outside tests).

697/0 both gates + all unit tests (rewrote the two Machine tests). Pure refactor, no
comptime behavior change.
This commit is contained in:
agra
2026-06-18 17:51:49 +03:00
parent 1526d198e2
commit 625ba0fb27
3 changed files with 80 additions and 75 deletions

View File

@@ -352,6 +352,23 @@ when reached (sentinels or accessor fns; see the design doc Risks).
`List` growth; orthogonal, see `current/CHECKPOINT-METATYPE.md`.)
## Log
- **Phase 4D.0 (VM plan) — comptime VM memory = an ARENA of stable host allocations; `Addr` = real host pointer (2026-06-18).**
Replaced the growable `ArrayList(u8)` flat buffer (which reallocs/MOVES on growth) with a
`std.heap.ArenaAllocator`: each `allocBytes` is a separate arena allocation that never moves and
is freed wholesale on `deinit` (no per-object free, no cap, no fixed buffer). **`Addr` is now the
allocation's absolute host pointer** (`@intFromPtr`), not an offset — so a flat-memory pointer and
an FFI-returned host pointer are the SAME kind of value, and the FFI bridge (4D.1) can pass them
to/from libc with ZERO translation and no per-call pinning (the original moving-buffer hazard is
gone by construction). `Machine.readWord/writeWord/bytes` deref the absolute pointer directly,
keeping the null-check bail (the malformed-IR / null-deref safety contract). Dropped the
offset-based upper-bounds check (can't bound an absolute pointer; the `Frame.bad_ref` guard still
catches the dominant malformed-IR vector) and the test-only `mark`/`reset` (the arena has no
cheap reset-to-mark; the VM never used them outside tests). Decision rationale (user): use a
GPA-like allocator, no artificial buffer limits. **697/0 BOTH gates + all unit tests** (rewrote
the two Machine tests: null-deref bail + arena-stability-across-grows). Pure refactor, no
comptime behavior change. **Next (4D.1):** extern-call dispatch in `Vm.invoke` — marshal args
(scalars by value, pointers as the host pointer they already are), call via `host_ffi`
trampolines, return scalars/pointers; a new `#run` libc example as the corpus guard.
- **Phase 4A.1 (VM plan) — `box_any`/`unbox_any` on the VM + `.any` as a 16-byte aggregate (2026-06-18).**
Ported the Any-boxing conversion pair: `box_any` allocates the 16-byte `{ type_tag@0, value@8 }`
box (tag = source TypeId index, matching the legacy comptime interp), writing a word source's