pr3.8
This commit is contained in:
parent
7feeabc1b6
commit
d24533cc62
@ -290,4 +290,34 @@ mod tests {
|
||||
// We can't access internal vector here, but stability is implied by handle not changing.
|
||||
assert_eq!(a.0, a.0); // placeholder sanity check
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sweep_reclaims_unrooted_cycle() {
|
||||
let mut heap = Heap::new();
|
||||
|
||||
// Build a 2-node cycle A <-> B using internal mutation (module-private access).
|
||||
let a = heap.allocate_array(vec![]);
|
||||
let b = heap.allocate_array(vec![]);
|
||||
|
||||
// Make A point to B and B point to A.
|
||||
if let Some(slot) = heap.objects.get_mut(a.0 as usize) {
|
||||
if let Some(obj) = slot.as_mut() {
|
||||
obj.array_elems = Some(vec![Value::HeapRef(b)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
}
|
||||
if let Some(slot) = heap.objects.get_mut(b.0 as usize) {
|
||||
if let Some(obj) = slot.as_mut() {
|
||||
obj.array_elems = Some(vec![Value::HeapRef(a)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// No roots: perform sweep directly; both should be reclaimed.
|
||||
heap.sweep();
|
||||
|
||||
assert!(!heap.is_valid(a));
|
||||
assert!(!heap.is_valid(b));
|
||||
assert_eq!(heap.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2537,9 +2537,10 @@ mod tests {
|
||||
fn test_gc_keeps_roots_and_collects_unreachable_at_frame_sync() {
|
||||
use crate::object::ObjectKind;
|
||||
|
||||
// ROM: FRAME_SYNC; HALT
|
||||
// ROM: FRAME_SYNC; FRAME_SYNC; HALT
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::FrameSync as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::FrameSync as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
@ -2571,4 +2572,88 @@ mod tests {
|
||||
assert!(vm.heap.is_valid(rooted));
|
||||
assert!(!vm.heap.is_valid(unreachable));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gc_simple_allocation_collection_cycle() {
|
||||
use crate::object::ObjectKind;
|
||||
|
||||
// ROM: FRAME_SYNC; FRAME_SYNC; HALT
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::FrameSync as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::FrameSync as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}]);
|
||||
|
||||
// Make GC trigger on any allocation delta
|
||||
vm.gc_alloc_threshold = 1;
|
||||
|
||||
// Cycle 1: allocate one unreachable object
|
||||
let _h1 = vm.heap.allocate_object(ObjectKind::Bytes, &[1]);
|
||||
assert_eq!(vm.heap.len(), 1);
|
||||
|
||||
let mut native = MockNative;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// FRAME_SYNC should collect it (first FRAME_SYNC)
|
||||
match vm.step(&mut native, &mut ctx) {
|
||||
Err(LogicalFrameEndingReason::FrameSync) => {}
|
||||
other => panic!("Expected FrameSync, got {:?}", other),
|
||||
}
|
||||
assert_eq!(vm.heap.len(), 0);
|
||||
|
||||
// Cycle 2: allocate again and collect again deterministically
|
||||
let _h2 = vm.heap.allocate_object(ObjectKind::Bytes, &[2]);
|
||||
assert_eq!(vm.heap.len(), 1);
|
||||
// Second FRAME_SYNC should also be reached deterministically
|
||||
match vm.step(&mut native, &mut ctx) {
|
||||
Err(LogicalFrameEndingReason::FrameSync) => {}
|
||||
other => panic!("Expected FrameSync, got {:?}", other),
|
||||
}
|
||||
assert_eq!(vm.heap.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gc_many_short_lived_objects_stress() {
|
||||
use crate::object::ObjectKind;
|
||||
|
||||
// ROM: FRAME_SYNC; HALT
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::FrameSync as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}]);
|
||||
|
||||
// Deterministic: trigger collection when any growth since last sweep occurs
|
||||
vm.gc_alloc_threshold = 1;
|
||||
|
||||
// Allocate many small, unreferenced objects
|
||||
let count = 2048usize; // stress but still quick
|
||||
for i in 0..count {
|
||||
let byte = (i & 0xFF) as u8;
|
||||
let _ = vm.heap.allocate_object(ObjectKind::Bytes, &[byte]);
|
||||
}
|
||||
assert_eq!(vm.heap.len(), count);
|
||||
|
||||
let mut native = MockNative;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// Single FRAME_SYNC should reclaim all since there are no roots
|
||||
match vm.step(&mut native, &mut ctx) {
|
||||
Err(LogicalFrameEndingReason::FrameSync) => {}
|
||||
other => panic!("Expected FrameSync, got {:?}", other),
|
||||
}
|
||||
|
||||
assert_eq!(vm.heap.len(), 0, "All short-lived objects must be reclaimed deterministically");
|
||||
}
|
||||
}
|
||||
|
||||
342
files/TODOs.md
342
files/TODOs.md
@ -1,47 +1,3 @@
|
||||
# PR-3.8 — GC Smoke and Stress Tests
|
||||
|
||||
### Briefing
|
||||
|
||||
We need confidence that the GC behaves correctly under simple and stressed conditions.
|
||||
|
||||
### Target
|
||||
|
||||
* Add deterministic smoke and stress tests for the GC.
|
||||
|
||||
### Work items
|
||||
|
||||
* Add tests:
|
||||
|
||||
* Simple allocation and collection cycle.
|
||||
* Many short-lived objects.
|
||||
* Cyclic references.
|
||||
* Ensure tests are deterministic.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] Smoke tests pass.
|
||||
* [ ] Stress tests pass.
|
||||
* [ ] No nondeterministic failures.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* New GC-specific tests.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Add deterministic tests.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Introduce random or timing-dependent tests.
|
||||
* Modify GC semantics to satisfy tests.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before changing test scenarios.
|
||||
# PR-4.1 — Define Canonical Stack Effect Table for Core ISA
|
||||
|
||||
### Briefing
|
||||
@ -396,3 +352,301 @@ We need a stable suite of valid and invalid bytecode samples to ensure verifier
|
||||
|
||||
* Ask before changing test expectations.
|
||||
|
||||
---
|
||||
|
||||
# PR-5.1 — Define Canonical Syscall Metadata Table
|
||||
|
||||
### Briefing
|
||||
|
||||
Syscalls must follow a unified, function-like ABI based on slot counts and capabilities. This PR introduces the canonical metadata table describing every syscall.
|
||||
|
||||
### Target
|
||||
|
||||
* Define a single authoritative metadata structure for syscalls.
|
||||
* Ensure all syscalls are described in terms of slot-based ABI.
|
||||
|
||||
### Work items
|
||||
|
||||
* Introduce a `SyscallMeta` struct containing:
|
||||
|
||||
* Syscall identifier.
|
||||
* `arg_slots`.
|
||||
* `ret_slots`.
|
||||
* Capability flags.
|
||||
* Determinism flags (if defined in spec).
|
||||
* Create a canonical syscall table/registry.
|
||||
* Ensure all existing syscalls are registered in this table.
|
||||
* Document the structure in code comments (English).
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] All syscalls have a `SyscallMeta` entry.
|
||||
* [ ] Metadata includes arg and return slot counts.
|
||||
* [ ] No syscall is invoked without metadata.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* Add a test ensuring all registered syscalls have metadata.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Introduce a syscall metadata struct.
|
||||
* Add a central registry for syscalls.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Change existing syscall semantics.
|
||||
* Add new syscalls.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before defining metadata fields.
|
||||
|
||||
---
|
||||
|
||||
# PR-5.2 — Implement Slot-Based Syscall Calling Convention
|
||||
|
||||
### Briefing
|
||||
|
||||
Syscalls must behave like functions using the stack-based slot ABI. This PR implements the unified calling convention.
|
||||
|
||||
### Target
|
||||
|
||||
* Ensure syscalls read arguments from the stack.
|
||||
* Ensure syscalls push return values based on `ret_slots`.
|
||||
|
||||
### Work items
|
||||
|
||||
* Modify the VM syscall dispatch logic to:
|
||||
|
||||
* Look up `SyscallMeta`.
|
||||
* Pop `arg_slots` from the stack.
|
||||
* Invoke the syscall.
|
||||
* Push `ret_slots` results.
|
||||
* Remove any legacy argument handling paths.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] All syscalls use slot-based calling convention.
|
||||
* [ ] Argument and return slot counts are enforced.
|
||||
* [ ] No legacy calling paths remain.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* Add tests:
|
||||
|
||||
* Syscall with correct arg/ret slots → passes.
|
||||
* Syscall with insufficient args → trap or verifier failure.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Refactor syscall dispatch code.
|
||||
* Use metadata to enforce slot counts.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Change the stack model.
|
||||
* Add compatibility layers.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before modifying dispatch semantics.
|
||||
|
||||
---
|
||||
|
||||
# PR-5.3 — Enforce Capability Checks in Syscall Dispatch
|
||||
|
||||
### Briefing
|
||||
|
||||
Syscalls must be capability-gated. The runtime must verify that the current program has permission to invoke each syscall.
|
||||
|
||||
### Target
|
||||
|
||||
* Enforce capability checks before syscall execution.
|
||||
|
||||
### Work items
|
||||
|
||||
* Extend `SyscallMeta` with capability requirements.
|
||||
* Add capability data to the VM or execution context.
|
||||
* In syscall dispatch:
|
||||
|
||||
* Check capabilities.
|
||||
* If missing, trigger `TRAP_INVALID_SYSCALL` or equivalent.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] Capability checks occur before syscall execution.
|
||||
* [ ] Missing capability leads to deterministic trap.
|
||||
* [ ] Valid capabilities allow execution.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* Add tests:
|
||||
|
||||
* Syscall without capability → trap.
|
||||
* Syscall with capability → success.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Add capability checks in dispatch.
|
||||
* Extend metadata with capability fields.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Change trap semantics.
|
||||
* Introduce dynamic or nondeterministic permission systems.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before defining capability behavior.
|
||||
|
||||
---
|
||||
|
||||
# PR-5.4 — Verifier Integration for Syscall Slot Rules
|
||||
|
||||
### Briefing
|
||||
|
||||
The verifier must ensure that syscall calls respect argument and return slot counts before runtime.
|
||||
|
||||
### Target
|
||||
|
||||
* Extend verifier to validate syscall usage.
|
||||
|
||||
### Work items
|
||||
|
||||
* At syscall call sites:
|
||||
|
||||
* Look up `SyscallMeta`.
|
||||
* Ensure enough argument slots are available.
|
||||
* Ensure stack shape after call matches `ret_slots`.
|
||||
* Emit verifier errors for mismatches.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] Verifier rejects incorrect syscall slot usage.
|
||||
* [ ] Correct programs pass.
|
||||
* [ ] Runtime traps are not required for verifier-detectable cases.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* Add tests:
|
||||
|
||||
* Too few args for syscall → verifier error.
|
||||
* Correct args/returns → passes.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Extend verifier with syscall checks.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Change runtime trap logic.
|
||||
* Add new trap categories.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before enforcing slot rules.
|
||||
|
||||
---
|
||||
|
||||
# PR-5.5 — Remove Legacy Syscall Entry Paths
|
||||
|
||||
### Briefing
|
||||
|
||||
Any old or experimental syscall entry paths must be removed so that the slot-based ABI is the only supported mechanism.
|
||||
|
||||
### Target
|
||||
|
||||
* Ensure only the new unified syscall dispatch path exists.
|
||||
|
||||
### Work items
|
||||
|
||||
* Search for legacy or alternate syscall invocation logic.
|
||||
* Remove or refactor them to use the canonical dispatch.
|
||||
* Update modules and exports accordingly.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] Only one syscall dispatch path remains.
|
||||
* [ ] No legacy syscall logic is present.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* Existing tests only.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Remove legacy syscall code paths.
|
||||
* Refactor callers to use the unified dispatch.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Introduce new syscall semantics.
|
||||
* Keep compatibility shims.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before deleting anything that looks externally visible.
|
||||
|
||||
---
|
||||
|
||||
# PR-5.6 — Syscall Multi-Return Tests
|
||||
|
||||
### Briefing
|
||||
|
||||
We must ensure multi-return syscalls behave correctly with the slot-based ABI.
|
||||
|
||||
### Target
|
||||
|
||||
* Add deterministic tests covering multi-return behavior.
|
||||
|
||||
### Work items
|
||||
|
||||
* Create or adapt at least one syscall with `ret_slots > 1`.
|
||||
* Add tests:
|
||||
|
||||
* Verify correct stack results after syscall.
|
||||
* Verify incorrect caller expectations fail verification.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] Multi-return syscalls behave correctly.
|
||||
* [ ] Verifier catches mismatches.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* New multi-return syscall tests.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Add deterministic tests.
|
||||
* Use existing syscalls or create a simple test-only syscall.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Modify syscall semantics to satisfy tests.
|
||||
* Add nondeterministic behavior.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before introducing new test syscalls.
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user