prometeu-runtime/docs/specs/pbs/Prometeu VM Memory model.md
Nilton Constantino 8c161e3e13
pr 35
2026-01-30 16:21:02 +00:00

360 lines
7.1 KiB
Markdown

# 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:
>
> 1. **Constant Pool** (read-only)
> 2. **Stack** (SAFE)
> 3. **Heap** (HIP storage bytes/slots)
> 4. **Gate Pool** (HIP handles, RC, metadata)
---
## 1. Design Goals
1. **Bytecode stability**: instruction meanings and data formats must remain stable across versions.
2. **Deterministic behavior**: no tracing GC; reclamation is defined by reference counts and safe points.
3. **Explicit costs**: HIP allocation and aliasing are explicit via gates.
4. **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):
```text
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:
* `GateId` is stable during the lifetime of an entry
* `GateId` values may be reused only after an entry is fully reclaimed (v0 may choose to never reuse)
* any invalid `GateId` access 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)` or `StringRef(ConstId)` (strings live in const pool)
* `Gate(GateId)`**this is the only HIP pointer form in v0**
* `Unit`
**Rule:** any former `Ref` pointer type must be reinterpreted as `GateId`.
---
## 5. Allocation (`alloc`) and Gate Creation
### 5.1 Concept
PBS `alloc` creates:
1. heap backing storage (N slots)
2. a gate pool entry
3. returns a gate handle onto the stack
### 5.2 Required inputs
Allocation must be shape-explicit:
* `TypeId` describing the allocated storage type
* `slots` describing the storage size
### 5.3 Runtime steps (normative)
On `ALLOC(type_id, slots)`:
1. allocate `slots` contiguous heap cells
2. create gate entry:
* `base = heap_index`
* `slots = slots`
* `strong_rc = 1`
* `type_id = type_id`
3. push `Gate(gate_id)` to stack
### 5.4 Example
PBS:
```pbs
let v: Box<Vector> = box(Vector.ZERO);
```
Lowering conceptually:
* compute value `Vector.ZERO` (SAFE)
* `ALLOC(TypeId(Vector), slots=2)` → returns `Gate(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:
1. gate validation
2. gate → (base, slots)
3. bounds check
4. 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:
1. mark gate entry `alive = false`
2. optionally add heap region to a free list
3. 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
```pbs
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:
* `a` and `b` are `GateId` copies
* mutation writes to the same heap storage
* `unbox(a)` peeks/copies storage into SAFE value
### 10.2 No HIP for strings
```pbs
let s: string = "hello";
```
* string literal lives in constant pool
* `s` is a SAFE value referencing `ConstId`
---
## 11. Conformance Checklist
A VM is conformant with this spec if:
* it implements the four memory regions
* `GateId` is the only HIP pointer form
* `ALLOC(type_id, slots)` returns `GateId`
* 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