pr 00.1 change run budget to remove hardware constraints
This commit is contained in:
parent
aab53d30be
commit
90095bb21e
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2238,6 +2238,7 @@ dependencies = [
|
||||
"prometeu-bytecode",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -71,9 +71,10 @@ fn test_integration_test01_link() {
|
||||
|
||||
let mut native = SimpleNative;
|
||||
let mut hw = SimpleHardware::new();
|
||||
let mut ctx = HostContext::new(Some(&mut hw));
|
||||
|
||||
// Run for a bit
|
||||
let report = vm.run_budget(1000, &mut native, &mut hw).expect("VM execution failed");
|
||||
let report = vm.run_budget(1000, &mut native, &mut ctx).expect("VM execution failed");
|
||||
|
||||
// It should not trap. test01 might loop or return.
|
||||
if let LogicalFrameEndingReason::Trap(t) = report.reason {
|
||||
|
||||
@ -8,4 +8,5 @@ license.workspace = true
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
prometeu-bytecode = { path = "../prometeu-bytecode" }
|
||||
prometeu-abi = { path = "../prometeu-abi" }
|
||||
prometeu-abi = { path = "../prometeu-abi" }
|
||||
url = "2.5.8"
|
||||
@ -181,7 +181,8 @@ impl PrometeuOS {
|
||||
|
||||
/// Executes a single VM instruction (Debug).
|
||||
pub fn debug_step_instruction(&mut self, vm: &mut VirtualMachine, hw: &mut dyn HardwareBridge) -> Option<String> {
|
||||
match vm.step(self, hw) {
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
match vm.step(self, &mut ctx) {
|
||||
Ok(_) => None,
|
||||
Err(e) => {
|
||||
let err_msg = format!("PVM Fault during Step: {:?}", e);
|
||||
@ -235,7 +236,10 @@ impl PrometeuOS {
|
||||
// 3. VM Execution
|
||||
if budget > 0 {
|
||||
// Run the VM until the budget is hit or FRAME_SYNC is reached.
|
||||
let run_result = vm.run_budget(budget, self, hw); // internally dispatch to frame on SDK
|
||||
let run_result = {
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
vm.run_budget(budget, self, &mut ctx)
|
||||
}; // internally dispatch to frame on SDK
|
||||
|
||||
match run_result {
|
||||
Ok(run) => {
|
||||
|
||||
@ -18,3 +18,14 @@ impl<'a> HostContext<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HostContextProvider {
|
||||
fn make_ctx<'a>(&'a mut self) -> HostContext<'a>;
|
||||
}
|
||||
|
||||
impl<T: HardwareBridge> HostContextProvider for T {
|
||||
#[inline]
|
||||
fn make_ctx<'a>(&'a mut self) -> HostContext<'a> {
|
||||
HostContext::new(Some(self))
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ pub mod opcode_spec;
|
||||
pub mod bytecode;
|
||||
pub mod verifier;
|
||||
|
||||
pub use host_context::HostContext;
|
||||
pub use host_context::{HostContext, HostContextProvider};
|
||||
pub use program::ProgramImage;
|
||||
pub use prometeu_bytecode::abi::TrapInfo;
|
||||
pub use prometeu_bytecode::opcode::OpCode;
|
||||
|
||||
@ -238,7 +238,7 @@ impl VirtualMachine {
|
||||
&mut self,
|
||||
budget: u64,
|
||||
native: &mut dyn NativeInterface,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
ctx: &mut HostContext,
|
||||
) -> Result<BudgetReport, String> {
|
||||
let start_cycles = self.cycles;
|
||||
let mut steps_executed = 0;
|
||||
@ -281,7 +281,7 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
// Execute a single step (Fetch-Decode-Execute)
|
||||
if let Err(reason) = self.step(native, hw) {
|
||||
if let Err(reason) = self.step(native, ctx) {
|
||||
ending_reason = Some(reason);
|
||||
break;
|
||||
}
|
||||
@ -331,7 +331,11 @@ impl VirtualMachine {
|
||||
/// 1. Fetch: Read the opcode from memory.
|
||||
/// 2. Decode: Identify what operation to perform.
|
||||
/// 3. Execute: Perform the operation, updating stacks, memory, or calling peripherals.
|
||||
pub fn step(&mut self, native: &mut dyn NativeInterface, hw: &mut dyn HardwareBridge) -> Result<(), LogicalFrameEndingReason> {
|
||||
pub fn step(&mut self, native: &mut dyn NativeInterface, ctx: &mut HostContext) -> Result<(), LogicalFrameEndingReason> {
|
||||
self.step_impl(native, ctx)
|
||||
}
|
||||
|
||||
fn step_impl(&mut self, native: &mut dyn NativeInterface, ctx: &mut HostContext) -> Result<(), LogicalFrameEndingReason> {
|
||||
if self.halted || self.pc >= self.program.rom.len() {
|
||||
return Ok(());
|
||||
}
|
||||
@ -843,8 +847,7 @@ impl VirtualMachine {
|
||||
|
||||
let stack_height_before = self.operand_stack.len();
|
||||
let mut ret = crate::virtual_machine::HostReturn::new(&mut self.operand_stack);
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
native.syscall(id, &args, &mut ret, &mut ctx).map_err(|fault| match fault {
|
||||
native.syscall(id, &args, &mut ret, ctx).map_err(|fault| match fault {
|
||||
crate::virtual_machine::VmFault::Trap(code, msg) => self.trap(code, OpCode::Syscall as u16, msg, pc_at_syscall),
|
||||
crate::virtual_machine::VmFault::Panic(msg) => LogicalFrameEndingReason::Panic(msg),
|
||||
crate::virtual_machine::VmFault::Unavailable => LogicalFrameEndingReason::Panic("Host feature unavailable".into()),
|
||||
@ -933,6 +936,7 @@ mod tests {
|
||||
use crate::virtual_machine::{expect_int, HostReturn, Value, VmFault};
|
||||
use prometeu_bytecode::abi::SourceSpan;
|
||||
use prometeu_bytecode::FunctionMeta;
|
||||
use url::Host;
|
||||
|
||||
struct MockNative;
|
||||
impl NativeInterface for MockNative {
|
||||
@ -941,24 +945,10 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
struct MockHardware;
|
||||
impl HardwareBridge for MockHardware {
|
||||
fn gfx(&self) -> &crate::hardware::Gfx { todo!() }
|
||||
fn gfx_mut(&mut self) -> &mut crate::hardware::Gfx { todo!() }
|
||||
fn audio(&self) -> &crate::hardware::Audio { todo!() }
|
||||
fn audio_mut(&mut self) -> &mut crate::hardware::Audio { todo!() }
|
||||
fn pad(&self) -> &crate::hardware::Pad { todo!() }
|
||||
fn pad_mut(&mut self) -> &mut crate::hardware::Pad { todo!() }
|
||||
fn touch(&self) -> &crate::hardware::Touch { todo!() }
|
||||
fn touch_mut(&mut self) -> &mut crate::hardware::Touch { todo!() }
|
||||
fn assets(&self) -> &crate::hardware::AssetManager { todo!() }
|
||||
fn assets_mut(&mut self) -> &mut crate::hardware::AssetManager { todo!() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic_chain() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// (10 + 20) * 2 / 5 % 4 = 12 * 2 / 5 % 4 = 60 / 5 % 4 = 12 % 4 = 0
|
||||
// wait: (10 + 20) = 30. 30 * 2 = 60. 60 / 5 = 12. 12 % 4 = 0.
|
||||
@ -980,7 +970,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(0));
|
||||
}
|
||||
@ -988,7 +978,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_div_by_zero_trap() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
@ -999,7 +989,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1013,7 +1003,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_int_to_bound_checked_trap() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
@ -1022,7 +1012,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1036,7 +1026,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_bounded_add_overflow_trap() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushBounded as u16).to_le_bytes());
|
||||
@ -1047,7 +1037,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1061,7 +1051,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_comparisons_polymorphic() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// 10 < 20.5 (true)
|
||||
let mut rom = Vec::new();
|
||||
@ -1073,7 +1063,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Boolean(true));
|
||||
}
|
||||
|
||||
@ -1086,9 +1076,9 @@ mod tests {
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Int64(42));
|
||||
}
|
||||
|
||||
@ -1101,9 +1091,9 @@ mod tests {
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Float(3.14));
|
||||
}
|
||||
|
||||
@ -1120,11 +1110,11 @@ mod tests {
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap(); // Push true
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push true
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Boolean(true));
|
||||
vm.step(&mut native, &mut hw).unwrap(); // Push false
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push false
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Boolean(false));
|
||||
}
|
||||
|
||||
@ -1138,9 +1128,9 @@ mod tests {
|
||||
let cp = vec![Value::String("hello".into())];
|
||||
let mut vm = VirtualMachine::new(rom, cp);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.peek().unwrap(), &Value::String("hello".into()));
|
||||
}
|
||||
|
||||
@ -1199,12 +1189,12 @@ mod tests {
|
||||
};
|
||||
vm.prepare_call("0");
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// Run until Halt
|
||||
let mut steps = 0;
|
||||
while !vm.halted && steps < 100 {
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
steps += 1;
|
||||
}
|
||||
|
||||
@ -1244,10 +1234,10 @@ mod tests {
|
||||
};
|
||||
vm.prepare_call("0");
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap(); // CALL
|
||||
let res = vm.step(&mut native, &mut hw); // RET -> should fail
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // CALL
|
||||
let res = vm.step(&mut native, &mut ctx); // RET -> should fail
|
||||
assert!(res.is_err());
|
||||
match res.unwrap_err() {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1282,9 +1272,9 @@ mod tests {
|
||||
..Default::default()
|
||||
};
|
||||
vm2.prepare_call("0");
|
||||
vm2.step(&mut native, &mut hw).unwrap(); // CALL
|
||||
vm2.step(&mut native, &mut hw).unwrap(); // PUSH_I64
|
||||
vm2.step(&mut native, &mut hw).unwrap(); // RET
|
||||
vm2.step(&mut native, &mut ctx).unwrap(); // CALL
|
||||
vm2.step(&mut native, &mut ctx).unwrap(); // PUSH_I64
|
||||
vm2.step(&mut native, &mut ctx).unwrap(); // RET
|
||||
|
||||
assert_eq!(vm2.operand_stack.len(), 1);
|
||||
assert_eq!(vm2.pop().unwrap(), Value::Int64(123));
|
||||
@ -1316,32 +1306,32 @@ mod tests {
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// Execute step by step and check stack
|
||||
vm.step(&mut native, &mut hw).unwrap(); // Push 1
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push 1
|
||||
assert_eq!(vm.operand_stack.len(), 1);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap(); // PushScope 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 hw).unwrap(); // Push 2
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push 2
|
||||
assert_eq!(vm.operand_stack.len(), 2);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap(); // PushScope 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 hw).unwrap(); // Push 3
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // Push 3
|
||||
assert_eq!(vm.operand_stack.len(), 3);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap(); // PopScope 2
|
||||
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 hw).unwrap(); // PopScope 1
|
||||
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));
|
||||
@ -1392,11 +1382,11 @@ mod tests {
|
||||
};
|
||||
vm.prepare_call("0");
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut steps = 0;
|
||||
while !vm.halted && steps < 100 {
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
steps += 1;
|
||||
}
|
||||
|
||||
@ -1415,16 +1405,16 @@ mod tests {
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Int32(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bitwise_promotion() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// i32 & i32 -> i32
|
||||
let mut rom = Vec::new();
|
||||
@ -1436,9 +1426,9 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(0));
|
||||
|
||||
// i32 | i64 -> i64
|
||||
@ -1451,16 +1441,16 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int64(0xFF));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comparisons_lte_gte() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// 10 <= 20 (true)
|
||||
let mut rom = Vec::new();
|
||||
@ -1472,9 +1462,9 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Boolean(true));
|
||||
|
||||
// 20 >= 20 (true)
|
||||
@ -1487,16 +1477,16 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Boolean(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
@ -1505,15 +1495,15 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(-42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jmp_if_true() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// Corrected Calculations:
|
||||
// 0-1: PushBool
|
||||
@ -1536,17 +1526,17 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap(); // PushBool
|
||||
vm.step(&mut native, &mut hw).unwrap(); // JmpIfTrue
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // PushBool
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // JmpIfTrue
|
||||
assert_eq!(vm.pc, 11);
|
||||
vm.step(&mut native, &mut hw).unwrap(); // PushI32
|
||||
vm.step(&mut native, &mut ctx).unwrap(); // PushI32
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trap_opcode() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
@ -1555,7 +1545,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Breakpoint);
|
||||
assert_eq!(vm.pc, 8); // PushI32 (6 bytes) + Trap (2 bytes)
|
||||
@ -1565,7 +1555,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pop_n_opcode() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
@ -1579,7 +1569,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(1));
|
||||
assert!(vm.pop().is_err()); // Stack should be empty
|
||||
@ -1588,7 +1578,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_hip_traps_oob() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// ALLOC int, 1 -> Gate(0)
|
||||
// GATE_LOAD 1 -> TRAP_OOB (size is 1, offset 1 is invalid)
|
||||
@ -1601,7 +1591,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1616,7 +1606,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_hip_traps_type() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// PUSH_I32 42
|
||||
// GATE_LOAD 0 -> TRAP_TYPE (Expected gate handle, got Int32)
|
||||
@ -1628,7 +1618,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1654,7 +1644,7 @@ mod tests {
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut hw = crate::Hardware::new();
|
||||
let mut ctx = HostContext::new(None);
|
||||
struct TestNative;
|
||||
impl NativeInterface for TestNative {
|
||||
fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { Ok(()) }
|
||||
@ -1662,7 +1652,7 @@ mod tests {
|
||||
let mut native = TestNative;
|
||||
|
||||
vm.prepare_call("0");
|
||||
let result = vm.run_budget(100, &mut native, &mut hw).expect("VM run failed");
|
||||
let result = vm.run_budget(100, &mut native, &mut ctx).expect("VM run failed");
|
||||
assert_eq!(result.reason, LogicalFrameEndingReason::EndOfRom);
|
||||
}
|
||||
|
||||
@ -1690,10 +1680,10 @@ mod tests {
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut native = MultiReturnNative;
|
||||
let mut hw = crate::Hardware::new();
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
assert_eq!(vm.pop().unwrap(), Value::Bounded(255));
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int64(42));
|
||||
@ -1721,11 +1711,11 @@ mod tests {
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut native = VoidReturnNative;
|
||||
let mut hw = crate::Hardware::new();
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
vm.operand_stack.push(Value::Int32(100));
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(100));
|
||||
assert!(vm.operand_stack.is_empty());
|
||||
@ -1756,10 +1746,10 @@ mod tests {
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut native = ArgCheckNative;
|
||||
let mut hw = crate::Hardware::new();
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1778,10 +1768,10 @@ mod tests {
|
||||
];
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1803,10 +1793,10 @@ mod tests {
|
||||
];
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -1840,10 +1830,10 @@ mod tests {
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = BadNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Panic(msg) => assert!(msg.contains("results mismatch")),
|
||||
_ => panic!("Expected Panic, got {:?}", report.reason),
|
||||
@ -1933,7 +1923,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_calling_convention_add() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// F0 (entry):
|
||||
// PUSH_I32 10
|
||||
@ -1985,7 +1975,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||
assert_eq!(vm.operand_stack.last().unwrap(), &Value::Int32(30));
|
||||
}
|
||||
@ -1993,7 +1983,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_calling_convention_multi_slot_return() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// F0:
|
||||
// CALL 1
|
||||
@ -2037,7 +2027,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||
// Stack should be [100, 200]
|
||||
assert_eq!(vm.operand_stack.len(), 2);
|
||||
@ -2048,7 +2038,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_calling_convention_void_call() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// F0:
|
||||
// PUSH_I32 42
|
||||
@ -2088,7 +2078,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||
assert_eq!(vm.operand_stack.len(), 0);
|
||||
}
|
||||
@ -2096,7 +2086,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_trap_invalid_func() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// CALL 99 (invalid)
|
||||
let mut rom = Vec::new();
|
||||
@ -2104,7 +2094,7 @@ mod tests {
|
||||
rom.extend_from_slice(&99u32.to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -2118,7 +2108,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_trap_bad_ret_slots() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// F0: CALL 1; HALT
|
||||
// F1: PUSH_I32 42; RET (expected 0 slots)
|
||||
@ -2153,7 +2143,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -2167,7 +2157,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_locals_round_trip() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// PUSH_I32 42
|
||||
// SET_LOCAL 0
|
||||
@ -2196,7 +2186,7 @@ mod tests {
|
||||
}]);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::EndOfRom);
|
||||
// RET pops return values and pushes them back on the caller stack (which is the sentinel frame's stack here).
|
||||
assert_eq!(vm.operand_stack, vec![Value::Int32(42)]);
|
||||
@ -2205,7 +2195,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_locals_per_call_isolation() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// Function 0 (entry):
|
||||
// CALL 1
|
||||
@ -2257,7 +2247,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||
|
||||
// The last value on stack is the return of the second CALL 1,
|
||||
@ -2268,7 +2258,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_invalid_local_index_traps() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// Function with 0 params, 1 local.
|
||||
// GET_LOCAL 1 (OOB)
|
||||
@ -2286,7 +2276,7 @@ mod tests {
|
||||
}]);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
@ -2301,7 +2291,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_nested_if() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// if (true) {
|
||||
// if (false) {
|
||||
@ -2361,14 +2351,14 @@ mod tests {
|
||||
}]);
|
||||
vm.prepare_call("0");
|
||||
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_with_empty_branches() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// PUSH_BOOL true
|
||||
// JMP_IF_FALSE -> ELSE (offset 15)
|
||||
@ -2399,7 +2389,7 @@ mod tests {
|
||||
}]);
|
||||
vm.prepare_call("0");
|
||||
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||
assert_eq!(vm.operand_stack.len(), 0);
|
||||
}
|
||||
@ -2407,7 +2397,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_jmp_if_non_boolean_trap() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
// PUSH_I32 1
|
||||
// JMP_IF_TRUE 9
|
||||
@ -2427,7 +2417,7 @@ mod tests {
|
||||
}]);
|
||||
vm.prepare_call("0");
|
||||
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
assert_eq!(trap.code, TRAP_TYPE);
|
||||
@ -2471,10 +2461,10 @@ mod tests {
|
||||
};
|
||||
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
assert_eq!(trap.code, TRAP_DIV_ZERO);
|
||||
@ -2518,10 +2508,10 @@ mod tests {
|
||||
};
|
||||
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.prepare_call("0");
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => {
|
||||
assert_eq!(trap.code, TRAP_DIV_ZERO);
|
||||
|
||||
@ -47,9 +47,10 @@ fn test_canonical_cartridge_heartbeat() {
|
||||
|
||||
let mut native = MockNative;
|
||||
let mut hw = Hardware::new();
|
||||
let mut ctx = HostContext::new(Some(&mut hw));
|
||||
|
||||
// Run for a reasonable budget
|
||||
let report = vm.run_budget(1000, &mut native, &mut hw).expect("VM failed to run");
|
||||
let report = vm.run_budget(1000, &mut native, &mut ctx).expect("VM failed to run");
|
||||
|
||||
// Acceptance criteria:
|
||||
// 1. No traps
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user