7.1 KiB
Prometeu VM Memory Model v0
Status: v0 (normative, implementer-facing)
Purpose: define the runtime memory model required to execute PBS programs with stable bytecode.
This specification describes the four memory regions and their interactions:
- Constant Pool (read-only)
- Stack (SAFE)
- Heap (HIP storage bytes/slots)
- Gate Pool (HIP handles, RC, metadata)
1. Design Goals
- Bytecode stability: instruction meanings and data formats must remain stable across versions.
- Deterministic behavior: no tracing GC; reclamation is defined by reference counts and safe points.
- Explicit costs: HIP allocation and aliasing are explicit via gates.
- PBS alignment: SAFE vs HIP semantics match PBS model.
2. Memory Regions Overview
2.1 Constant Pool (RO)
A program-wide immutable pool containing:
- integers, floats, bounded ints
- strings
- (optional in future) constant composite literals
Properties:
- read-only during execution
- indexed by
ConstId - VM bytecode uses
PUSH_CONST(ConstId)
2.2 Stack (SAFE)
The stack contains:
- local variables (by slot)
- operand stack values for instruction evaluation
SAFE properties:
- values are copied by value
- no aliasing across variables unless the value is a gate handle
- stack values are reclaimed automatically when frames unwind
2.3 Heap (HIP storage)
The heap is a contiguous array of machine slots (e.g., Value slots), used only as storage backing for HIP objects.
Heap properties:
- heap cells are not directly addressable by bytecode
- heap is accessed only via Gate Pool resolution
The heap may implement:
- bump allocation (v0)
- free list (optional)
- compaction is not required in v0
2.4 Gate Pool (HIP handles)
The gate pool is the authoritative table mapping a small integer handle (GateId) to a storage object.
Gate Pool entry (conceptual):
GateEntry {
alive: bool,
base: HeapIndex,
slots: u32,
strong_rc: u32,
weak_rc: u32, // optional in v0; may be reserved
type_id: TypeId, // required for layout + debug
flags: GateFlags,
}
Properties:
GateIdis stable during the lifetime of an entryGateIdvalues may be reused only after an entry is fully reclaimed (v0 may choose to never reuse)- any invalid
GateIdaccess is a runtime trap (deterministic)
3. SAFE vs HIP
3.1 SAFE
SAFE is stack-only execution:
- primitives
- structs / arrays as value copies
- temporaries
SAFE values are always reclaimed by frame unwinding.
3.2 HIP
HIP is heap-backed storage:
- storage objects allocated with
alloc - accessed through gates
- aliasing occurs by copying a gate handle
HIP values are reclaimed by reference counting.
4. Value Representation
4.1 Stack Values
A VM Value type must minimally support:
Int(i64)Float(f64)Bounded(u32)Bool(bool)String(ConstId)orStringRef(ConstId)(strings live in const pool)Gate(GateId)← this is the only HIP pointer form in v0Unit
Rule: any former Ref pointer type must be reinterpreted as GateId.
5. Allocation (alloc) and Gate Creation
5.1 Concept
PBS alloc creates:
- heap backing storage (N slots)
- a gate pool entry
- returns a gate handle onto the stack
5.2 Required inputs
Allocation must be shape-explicit:
TypeIddescribing the allocated storage typeslotsdescribing the storage size
5.3 Runtime steps (normative)
On ALLOC(type_id, slots):
-
allocate
slotscontiguous heap cells -
create gate entry:
base = heap_indexslots = slotsstrong_rc = 1type_id = type_id
-
push
Gate(gate_id)to stack
5.4 Example
PBS:
let v: Box<Vector> = box(Vector.ZERO);
Lowering conceptually:
- compute value
Vector.ZERO(SAFE) ALLOC(TypeId(Vector), slots=2)→ returnsGate(g0)- store the two fields into heap via
STORE_GATE_FIELD
6. Gate Access (Read/Write)
6.1 Access Principle
Heap is never accessed directly. All reads/writes go through:
- gate validation
- gate → (base, slots)
- bounds check
- heap read/write
6.2 Read / Peek
peek copies from HIP storage to SAFE value.
- no RC changes
- no aliasing is created
6.3 Borrow (read-only view)
Borrow provides temporary read-only access.
- runtime may enforce with a borrow stack (debug)
- v0 may treat borrow as a checked read scope
6.4 Mutate (mutable view)
Mutate provides temporary mutable access.
-
v0 may treat mutate as:
- read into scratch (SAFE)
- write back on
EndMutate
Or (preferred later):
- direct heap writes within a guarded scope
7. Reference Counting (RC)
7.1 Strong RC
Strong RC counts how many live gate handles exist.
A GateId is considered live if it exists in:
- a stack slot
- a global slot
- a heap storage cell (HIP) (future refinement)
7.2 RC operations
When copying a gate handle into a new location:
- increment
strong_rc
When a gate handle is removed/overwritten:
- decrement
strong_rc
Rule: RC updates are required for any VM instruction that:
- assigns locals/globals
- stores into heap cells
- pops stack values
7.3 Release and Reclamation
When strong_rc reaches 0:
- gate entry becomes eligible for reclamation
- actual reclamation occurs at a safe point
Safe points (v0):
- end of frame
- explicit
FRAME_SYNC(if present)
Reclamation:
- mark gate entry
alive = false - optionally add heap region to a free list
- gate id may be recycled (optional)
No tracing GC is performed.
8. Weak Gates (Reserved / Optional)
v0 may reserve the field weak_rc but does not require full weak semantics.
If implemented:
- weak handles do not keep storage alive
- upgrading weak → strong requires a runtime check
9. Runtime Traps (Deterministic)
The VM must trap deterministically on:
- invalid
GateId - accessing a dead gate
- out-of-bounds offset
- type mismatch in a typed store/load (if enforced)
Traps must include:
- opcode
- span (if debug info present)
- message
10. Examples
10.1 Aliasing via gates
let a: Box<Vector> = box(Vector.ZERO);
let b: Box<Vector> = a; // copy handle, RC++
mutate b {
it.x += 10;
}
let v: Vector = unbox(a); // observes mutation
Explanation:
aandbareGateIdcopies- mutation writes to the same heap storage
unbox(a)peeks/copies storage into SAFE value
10.2 No HIP for strings
let s: string = "hello";
- string literal lives in constant pool
sis a SAFE value referencingConstId
11. Conformance Checklist
A VM is conformant with this spec if:
- it implements the four memory regions
GateIdis the only HIP pointer formALLOC(type_id, slots)returnsGateId- heap access is only via gate resolution
- RC increments/decrements occur on gate copies/drops
- reclamation happens only at safe points
Appendix A — Implementation Notes (Non-normative)
- Start with bump-alloc heap + never-reuse GateIds (simplest v0)
- Add free list later
- Add borrow/mutate enforcement later as debug-only checks