pr2.1
This commit is contained in:
parent
a5daebd849
commit
8b744a120e
@ -1,6 +1,5 @@
|
||||
mod call_frame;
|
||||
pub mod local_addressing;
|
||||
mod scope_frame;
|
||||
pub mod verifier;
|
||||
mod virtual_machine;
|
||||
pub mod vm_init_error;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use crate::call_frame::CallFrame;
|
||||
use crate::scope_frame::ScopeFrame;
|
||||
use crate::verifier::Verifier;
|
||||
use crate::vm_init_error::VmInitError;
|
||||
use crate::{HostContext, NativeInterface};
|
||||
@ -79,16 +78,12 @@ pub struct VirtualMachine {
|
||||
pub operand_stack: Vec<Value>,
|
||||
/// Call Stack: Manages function call context (return addresses, frame limits).
|
||||
pub call_stack: Vec<CallFrame>,
|
||||
/// Scope Stack: Handles block-level local variable visibility (scopes).
|
||||
pub scope_stack: Vec<ScopeFrame>,
|
||||
/// Global Variable Store: Variables that persist for the lifetime of the program.
|
||||
pub globals: Vec<Value>,
|
||||
/// The loaded executable (Bytecode + Constant Pool), that is the ROM translated.
|
||||
pub program: ProgramImage,
|
||||
/// Heap Memory: Dynamic allocation pool.
|
||||
pub heap: Vec<Value>,
|
||||
/// Gate Pool: indirection table for heap objects. Value::Gate carries an index into this pool.
|
||||
pub gate_pool: Vec<GateEntry>,
|
||||
/// Total virtual cycles consumed since the VM started.
|
||||
pub cycles: u64,
|
||||
/// Stop flag: true if a `HALT` opcode was encountered.
|
||||
@ -97,22 +92,7 @@ pub struct VirtualMachine {
|
||||
pub breakpoints: std::collections::HashSet<usize>,
|
||||
}
|
||||
|
||||
/// Identifier for a gate (index into `gate_pool`).
|
||||
/// We keep using `Value::Gate(usize)` in the ABI, but inside the VM this represents a `GateId`.
|
||||
pub type GateId = u32;
|
||||
|
||||
/// Metadata for a gate entry, which resolves a `GateId` into a slice of the heap.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GateEntry {
|
||||
/// True if this entry is currently alive and usable.
|
||||
pub alive: bool,
|
||||
/// Base index into the heap vector where this gate's slots start.
|
||||
pub base: u32,
|
||||
/// Number of heap slots reserved for this gate.
|
||||
pub slots: u32,
|
||||
/// Type identifier for the gate's storage (kept for future checks; unused for now).
|
||||
pub type_id: u32,
|
||||
}
|
||||
// HIP/Gate runtime structures removed per PR-2.1
|
||||
|
||||
impl Default for VirtualMachine {
|
||||
fn default() -> Self {
|
||||
@ -127,7 +107,6 @@ impl VirtualMachine {
|
||||
pc: 0,
|
||||
operand_stack: Vec::new(),
|
||||
call_stack: Vec::new(),
|
||||
scope_stack: Vec::new(),
|
||||
globals: Vec::new(),
|
||||
program: ProgramImage::new(
|
||||
rom,
|
||||
@ -137,7 +116,6 @@ impl VirtualMachine {
|
||||
std::collections::HashMap::new(),
|
||||
),
|
||||
heap: Vec::new(),
|
||||
gate_pool: Vec::new(),
|
||||
cycles: 0,
|
||||
halted: false,
|
||||
breakpoints: std::collections::HashSet::new(),
|
||||
@ -157,10 +135,8 @@ impl VirtualMachine {
|
||||
self.pc = 0;
|
||||
self.operand_stack.clear();
|
||||
self.call_stack.clear();
|
||||
self.scope_stack.clear();
|
||||
self.globals.clear();
|
||||
self.heap.clear();
|
||||
self.gate_pool.clear();
|
||||
self.cycles = 0;
|
||||
self.halted = true; // execution is impossible until a successful load
|
||||
|
||||
@ -244,7 +220,6 @@ impl VirtualMachine {
|
||||
// cause the VM to stop after returning from the entrypoint.
|
||||
self.operand_stack.clear();
|
||||
self.call_stack.clear();
|
||||
self.scope_stack.clear();
|
||||
|
||||
// Entrypoint also needs locals allocated.
|
||||
// For the sentinel frame, stack_base is always 0.
|
||||
@ -961,15 +936,9 @@ impl VirtualMachine {
|
||||
}
|
||||
self.pc = frame.return_pc as usize;
|
||||
}
|
||||
OpCode::PushScope => {
|
||||
self.scope_stack.push(ScopeFrame { scope_stack_base: self.operand_stack.len() });
|
||||
}
|
||||
OpCode::PopScope => {
|
||||
let frame = self.scope_stack.pop().ok_or_else(|| {
|
||||
LogicalFrameEndingReason::Panic("Scope stack underflow".into())
|
||||
})?;
|
||||
self.operand_stack.truncate(frame.scope_stack_base);
|
||||
}
|
||||
// Scope opcodes are no-ops under the new architecture (PR-2.1)
|
||||
OpCode::PushScope => {}
|
||||
OpCode::PopScope => {}
|
||||
OpCode::Syscall => {
|
||||
let pc_at_syscall = start_pc as u32;
|
||||
let id = instr
|
||||
@ -1034,28 +1003,6 @@ impl VirtualMachine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolves a `GateId` to an immutable reference to its `GateEntry` or returns a trap reason.
|
||||
fn resolve_gate(
|
||||
&self,
|
||||
gate_id: GateId,
|
||||
opcode: u16,
|
||||
pc: u32,
|
||||
) -> Result<&GateEntry, LogicalFrameEndingReason> {
|
||||
let idx = gate_id as usize;
|
||||
let entry = self.gate_pool.get(idx).ok_or_else(|| {
|
||||
self.trap(TRAP_OOB, opcode, format!("Invalid gate id: {}", gate_id), pc)
|
||||
})?;
|
||||
if !entry.alive {
|
||||
return Err(self.trap(
|
||||
TRAP_OOB,
|
||||
opcode,
|
||||
format!("Dead gate id: {}", gate_id),
|
||||
pc,
|
||||
));
|
||||
}
|
||||
Ok(entry)
|
||||
}
|
||||
|
||||
pub fn trap(
|
||||
&self,
|
||||
code: u32,
|
||||
@ -1407,7 +1354,7 @@ mod tests {
|
||||
assert_eq!(vm.pop_integer().unwrap(), 30);
|
||||
assert_eq!(vm.operand_stack.len(), 0);
|
||||
assert_eq!(vm.call_stack.len(), 1);
|
||||
assert_eq!(vm.scope_stack.len(), 0);
|
||||
// Scope frames removed: no scope stack to assert on
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1497,127 +1444,7 @@ mod tests {
|
||||
assert_eq!(vm2.pop().unwrap(), Value::Int64(123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_scopes() {
|
||||
let mut rom = Vec::new();
|
||||
|
||||
// PUSH_I64 1
|
||||
// PUSH_SCOPE
|
||||
// PUSH_I64 2
|
||||
// PUSH_SCOPE
|
||||
// PUSH_I64 3
|
||||
// POP_SCOPE
|
||||
// POP_SCOPE
|
||||
// HALT
|
||||
rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&1i64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushScope as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&2i64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushScope as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&3i64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PopScope as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PopScope 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![]);
|
||||
let mut native = MockNative;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// Execute step by step and check stack
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push 1
|
||||
assert_eq!(vm.operand_stack.len(), 1);
|
||||
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // PushScope 1
|
||||
assert_eq!(vm.scope_stack.len(), 1);
|
||||
assert_eq!(vm.scope_stack.last().unwrap().scope_stack_base, 1);
|
||||
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push 2
|
||||
assert_eq!(vm.operand_stack.len(), 2);
|
||||
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // PushScope 2
|
||||
assert_eq!(vm.scope_stack.len(), 2);
|
||||
assert_eq!(vm.scope_stack.last().unwrap().scope_stack_base, 2);
|
||||
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push 3
|
||||
assert_eq!(vm.operand_stack.len(), 3);
|
||||
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // PopScope 2
|
||||
assert_eq!(vm.scope_stack.len(), 1);
|
||||
assert_eq!(vm.operand_stack.len(), 2);
|
||||
assert_eq!(vm.operand_stack.last().unwrap(), &Value::Int64(2));
|
||||
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // PopScope 1
|
||||
assert_eq!(vm.scope_stack.len(), 0);
|
||||
assert_eq!(vm.operand_stack.len(), 1);
|
||||
assert_eq!(vm.operand_stack.last().unwrap(), &Value::Int64(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pop_scope_does_not_affect_ret() {
|
||||
let mut rom = Vec::new();
|
||||
|
||||
// PUSH_I64 100
|
||||
// CALL func_id 1
|
||||
// HALT
|
||||
rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&100i64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Call as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&1u32.to_le_bytes()); // func_id 1
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let func_addr = rom.len();
|
||||
// func:
|
||||
// PUSH_I64 200
|
||||
// PUSH_SCOPE
|
||||
// PUSH_I64 300
|
||||
// RET
|
||||
|
||||
rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&200i64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushScope as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&300i64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PopScope as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
|
||||
let functions = vec![
|
||||
FunctionMeta { code_offset: 0, code_len: func_addr as u32, ..Default::default() },
|
||||
FunctionMeta {
|
||||
code_offset: func_addr as u32,
|
||||
code_len: (rom.len() - func_addr) as u32,
|
||||
param_slots: 0,
|
||||
return_slots: 1,
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
|
||||
let mut vm = VirtualMachine {
|
||||
program: ProgramImage::new(
|
||||
rom,
|
||||
vec![],
|
||||
functions,
|
||||
None,
|
||||
std::collections::HashMap::new(),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
vm.prepare_call("0");
|
||||
let mut native = MockNative;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut steps = 0;
|
||||
while !vm.halted && steps < 100 {
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
steps += 1;
|
||||
}
|
||||
|
||||
assert!(vm.halted);
|
||||
assert_eq!(vm.operand_stack.len(), 2);
|
||||
assert_eq!(vm.operand_stack[0], Value::Int64(100));
|
||||
assert_eq!(vm.operand_stack[1], Value::Int64(200));
|
||||
}
|
||||
// Scope tests removed under PR-2.1 (scope frames eliminated)
|
||||
|
||||
#[test]
|
||||
fn test_push_i32() {
|
||||
|
||||
@ -1,51 +1,3 @@
|
||||
# PR-2.1 — Remove ScopeFrame and HIP Runtime Structures
|
||||
|
||||
### Briefing
|
||||
|
||||
The new architecture removes HIP, borrow/mutate/peek semantics, and any gate-based lifetime tracking. The VM must no longer depend on `ScopeFrame` or related structures.
|
||||
|
||||
### Target
|
||||
|
||||
* Remove `ScopeFrame` and any HIP-related runtime data structures.
|
||||
* Ensure the VM compiles and runs without any scope/gate logic.
|
||||
|
||||
### Work items
|
||||
|
||||
* Delete `scope_frame.rs` and any modules dedicated to HIP or gate lifetimes.
|
||||
* Remove fields in VM state that track scope frames, gates, or borrow state.
|
||||
* Update the main VM execution loop to no longer push/pop scope frames.
|
||||
* Remove any trap logic that references scope or gate violations.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] `ScopeFrame` and related modules are fully removed.
|
||||
* [ ] VM compiles without scope/gate concepts.
|
||||
* [ ] No HIP-related symbols remain in the VM crate.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* Existing tests only.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Delete scope/gate-related modules and fields.
|
||||
* Update code to remove references to them.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Introduce new lifetime or ownership systems.
|
||||
* Replace scope frames with another architecture.
|
||||
* Add compatibility layers.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask for clarification instead of inventing new runtime concepts.
|
||||
|
||||
---
|
||||
|
||||
# PR-2.2 — Simplify VM Execution Loop (Pure Stack Machine)
|
||||
|
||||
### Briefing
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user