pr2.1
This commit is contained in:
parent
a5daebd849
commit
8b744a120e
@ -1,6 +1,5 @@
|
|||||||
mod call_frame;
|
mod call_frame;
|
||||||
pub mod local_addressing;
|
pub mod local_addressing;
|
||||||
mod scope_frame;
|
|
||||||
pub mod verifier;
|
pub mod verifier;
|
||||||
mod virtual_machine;
|
mod virtual_machine;
|
||||||
pub mod vm_init_error;
|
pub mod vm_init_error;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use crate::call_frame::CallFrame;
|
use crate::call_frame::CallFrame;
|
||||||
use crate::scope_frame::ScopeFrame;
|
|
||||||
use crate::verifier::Verifier;
|
use crate::verifier::Verifier;
|
||||||
use crate::vm_init_error::VmInitError;
|
use crate::vm_init_error::VmInitError;
|
||||||
use crate::{HostContext, NativeInterface};
|
use crate::{HostContext, NativeInterface};
|
||||||
@ -79,16 +78,12 @@ pub struct VirtualMachine {
|
|||||||
pub operand_stack: Vec<Value>,
|
pub operand_stack: Vec<Value>,
|
||||||
/// Call Stack: Manages function call context (return addresses, frame limits).
|
/// Call Stack: Manages function call context (return addresses, frame limits).
|
||||||
pub call_stack: Vec<CallFrame>,
|
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.
|
/// Global Variable Store: Variables that persist for the lifetime of the program.
|
||||||
pub globals: Vec<Value>,
|
pub globals: Vec<Value>,
|
||||||
/// The loaded executable (Bytecode + Constant Pool), that is the ROM translated.
|
/// The loaded executable (Bytecode + Constant Pool), that is the ROM translated.
|
||||||
pub program: ProgramImage,
|
pub program: ProgramImage,
|
||||||
/// Heap Memory: Dynamic allocation pool.
|
/// Heap Memory: Dynamic allocation pool.
|
||||||
pub heap: Vec<Value>,
|
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.
|
/// Total virtual cycles consumed since the VM started.
|
||||||
pub cycles: u64,
|
pub cycles: u64,
|
||||||
/// Stop flag: true if a `HALT` opcode was encountered.
|
/// Stop flag: true if a `HALT` opcode was encountered.
|
||||||
@ -97,22 +92,7 @@ pub struct VirtualMachine {
|
|||||||
pub breakpoints: std::collections::HashSet<usize>,
|
pub breakpoints: std::collections::HashSet<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identifier for a gate (index into `gate_pool`).
|
// HIP/Gate runtime structures removed per PR-2.1
|
||||||
/// 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for VirtualMachine {
|
impl Default for VirtualMachine {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -127,7 +107,6 @@ impl VirtualMachine {
|
|||||||
pc: 0,
|
pc: 0,
|
||||||
operand_stack: Vec::new(),
|
operand_stack: Vec::new(),
|
||||||
call_stack: Vec::new(),
|
call_stack: Vec::new(),
|
||||||
scope_stack: Vec::new(),
|
|
||||||
globals: Vec::new(),
|
globals: Vec::new(),
|
||||||
program: ProgramImage::new(
|
program: ProgramImage::new(
|
||||||
rom,
|
rom,
|
||||||
@ -137,7 +116,6 @@ impl VirtualMachine {
|
|||||||
std::collections::HashMap::new(),
|
std::collections::HashMap::new(),
|
||||||
),
|
),
|
||||||
heap: Vec::new(),
|
heap: Vec::new(),
|
||||||
gate_pool: Vec::new(),
|
|
||||||
cycles: 0,
|
cycles: 0,
|
||||||
halted: false,
|
halted: false,
|
||||||
breakpoints: std::collections::HashSet::new(),
|
breakpoints: std::collections::HashSet::new(),
|
||||||
@ -157,10 +135,8 @@ impl VirtualMachine {
|
|||||||
self.pc = 0;
|
self.pc = 0;
|
||||||
self.operand_stack.clear();
|
self.operand_stack.clear();
|
||||||
self.call_stack.clear();
|
self.call_stack.clear();
|
||||||
self.scope_stack.clear();
|
|
||||||
self.globals.clear();
|
self.globals.clear();
|
||||||
self.heap.clear();
|
self.heap.clear();
|
||||||
self.gate_pool.clear();
|
|
||||||
self.cycles = 0;
|
self.cycles = 0;
|
||||||
self.halted = true; // execution is impossible until a successful load
|
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.
|
// cause the VM to stop after returning from the entrypoint.
|
||||||
self.operand_stack.clear();
|
self.operand_stack.clear();
|
||||||
self.call_stack.clear();
|
self.call_stack.clear();
|
||||||
self.scope_stack.clear();
|
|
||||||
|
|
||||||
// Entrypoint also needs locals allocated.
|
// Entrypoint also needs locals allocated.
|
||||||
// For the sentinel frame, stack_base is always 0.
|
// For the sentinel frame, stack_base is always 0.
|
||||||
@ -961,15 +936,9 @@ impl VirtualMachine {
|
|||||||
}
|
}
|
||||||
self.pc = frame.return_pc as usize;
|
self.pc = frame.return_pc as usize;
|
||||||
}
|
}
|
||||||
OpCode::PushScope => {
|
// Scope opcodes are no-ops under the new architecture (PR-2.1)
|
||||||
self.scope_stack.push(ScopeFrame { scope_stack_base: self.operand_stack.len() });
|
OpCode::PushScope => {}
|
||||||
}
|
OpCode::PopScope => {}
|
||||||
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);
|
|
||||||
}
|
|
||||||
OpCode::Syscall => {
|
OpCode::Syscall => {
|
||||||
let pc_at_syscall = start_pc as u32;
|
let pc_at_syscall = start_pc as u32;
|
||||||
let id = instr
|
let id = instr
|
||||||
@ -1034,28 +1003,6 @@ impl VirtualMachine {
|
|||||||
Ok(())
|
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(
|
pub fn trap(
|
||||||
&self,
|
&self,
|
||||||
code: u32,
|
code: u32,
|
||||||
@ -1407,7 +1354,7 @@ mod tests {
|
|||||||
assert_eq!(vm.pop_integer().unwrap(), 30);
|
assert_eq!(vm.pop_integer().unwrap(), 30);
|
||||||
assert_eq!(vm.operand_stack.len(), 0);
|
assert_eq!(vm.operand_stack.len(), 0);
|
||||||
assert_eq!(vm.call_stack.len(), 1);
|
assert_eq!(vm.call_stack.len(), 1);
|
||||||
assert_eq!(vm.scope_stack.len(), 0);
|
// Scope frames removed: no scope stack to assert on
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1497,127 +1444,7 @@ mod tests {
|
|||||||
assert_eq!(vm2.pop().unwrap(), Value::Int64(123));
|
assert_eq!(vm2.pop().unwrap(), Value::Int64(123));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// Scope tests removed under PR-2.1 (scope frames eliminated)
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_push_i32() {
|
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)
|
# PR-2.2 — Simplify VM Execution Loop (Pure Stack Machine)
|
||||||
|
|
||||||
### Briefing
|
### Briefing
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user