prometeu-runtime/crates/core/src/vm/virtual_machine.rs
2026-01-16 13:05:42 +00:00

401 lines
15 KiB
Rust

use crate::hardware::HardwareBridge;
use crate::prometeu_os::NativeInterface;
use crate::vm::call_frame::CallFrame;
use crate::vm::opcode::OpCode;
use crate::vm::value::Value;
use crate::vm::Program;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogicalFrameEndingReason {
FrameSync,
BudgetExhausted,
Halted,
EndOfRom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BudgetReport {
pub cycles_used: u64,
pub reason: LogicalFrameEndingReason,
}
pub struct VirtualMachine {
pub pc: usize,
pub operand_stack: Vec<Value>,
pub call_stack: Vec<CallFrame>,
pub globals: Vec<Value>,
pub program: Program,
pub heap: Vec<Value>, // Simplificado para demo, futuramente RAM/Heap real
pub cycles: u64,
pub halted: bool,
}
impl VirtualMachine {
pub fn new(rom: Vec<u8>, constant_pool: Vec<Value>) -> Self {
Self {
pc: 0,
operand_stack: Vec::new(),
call_stack: Vec::new(),
globals: Vec::new(),
program: Program::new(rom, constant_pool),
heap: Vec::new(),
cycles: 0,
halted: false,
}
}
}
impl Default for VirtualMachine {
fn default() -> Self {
Self::new(vec![], vec![])
}
}
impl VirtualMachine {
pub fn run_budget(
&mut self,
budget: u64,
native: &mut dyn NativeInterface,
hw: &mut dyn HardwareBridge,
) -> Result<BudgetReport, String> {
let start_cycles = self.cycles;
let mut ending_reason: Option<LogicalFrameEndingReason> = None;
while (self.cycles - start_cycles) < budget
&& !self.halted
&& self.pc < self.program.rom.len()
{
let pc_before = self.pc;
let cycles_before = self.cycles;
// Fast-path: FRAME_SYNC encerra o frame lógico
let opcode_val = self.peek_u16()?;
let opcode = OpCode::try_from(opcode_val)?;
if opcode == OpCode::FrameSync {
self.pc += 2;
self.cycles += OpCode::FrameSync.cycles();
ending_reason = Some(LogicalFrameEndingReason::FrameSync);
break;
}
self.step(native, hw)?;
// garante progresso real
if self.pc == pc_before && self.cycles == cycles_before && !self.halted {
return Err(format!("VM stuck at PC 0x{:08X}", self.pc));
}
}
if ending_reason.is_none() {
if self.halted {
ending_reason = Some(LogicalFrameEndingReason::Halted);
} else if self.pc >= self.program.rom.len() {
ending_reason = Some(LogicalFrameEndingReason::EndOfRom);
} else {
ending_reason = Some(LogicalFrameEndingReason::BudgetExhausted);
}
}
Ok(BudgetReport {
cycles_used: self.cycles - start_cycles,
reason: ending_reason.unwrap(),
})
}
fn peek_u16(&self) -> Result<u16, String> {
if self.pc + 2 > self.program.rom.len() {
return Err("Unexpected end of ROM".into());
}
let bytes = [
self.program.rom[self.pc],
self.program.rom[self.pc + 1],
];
Ok(u16::from_le_bytes(bytes))
}
pub fn step(&mut self, native: &mut dyn NativeInterface, hw: &mut dyn HardwareBridge) -> Result<(), String> {
if self.halted || self.pc >= self.program.rom.len() {
return Ok(());
}
let opcode_val = self.read_u16()?;
let opcode = OpCode::try_from(opcode_val)?;
match opcode {
OpCode::Nop => {}
OpCode::Halt => {
self.halted = true;
}
OpCode::Jmp => {
let addr = self.read_u32()? as usize;
self.pc = addr;
}
OpCode::JmpIfFalse => {
let addr = self.read_u32()? as usize;
let val = self.pop()?;
if let Value::Boolean(false) = val {
self.pc = addr;
}
}
OpCode::PushConst => {
let idx = self.read_u32()? as usize;
let val = self.program.constant_pool.get(idx).cloned().ok_or("Invalid constant index")?;
self.push(val);
}
OpCode::Pop => {
self.pop()?;
}
OpCode::Dup => {
let val = self.peek()?.clone();
self.push(val);
}
OpCode::Swap => {
let a = self.pop()?;
let b = self.pop()?;
self.push(a);
self.push(b);
}
OpCode::Add => self.binary_op(|a, b| match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_add(b))),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 + b)),
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a + b as f64)),
_ => Err("Invalid types for ADD".into()),
})?,
OpCode::Sub => self.binary_op(|a, b| match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_sub(b))),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 - b)),
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a - b as f64)),
_ => Err("Invalid types for SUB".into()),
})?,
OpCode::Mul => self.binary_op(|a, b| match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_mul(b))),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 * b)),
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a * b as f64)),
_ => Err("Invalid types for MUL".into()),
})?,
OpCode::Div => self.binary_op(|a, b| match (a, b) {
(Value::Integer(a), Value::Integer(b)) => {
if b == 0 { return Err("Division by zero".into()); }
Ok(Value::Integer(a / b))
}
(Value::Float(a), Value::Float(b)) => {
if b == 0.0 { return Err("Division by zero".into()); }
Ok(Value::Float(a / b))
}
(Value::Integer(a), Value::Float(b)) => {
if b == 0.0 { return Err("Division by zero".into()); }
Ok(Value::Float(a as f64 / b))
}
(Value::Float(a), Value::Integer(b)) => {
if b == 0 { return Err("Division by zero".into()); }
Ok(Value::Float(a / b as f64))
}
_ => Err("Invalid types for DIV".into()),
})?,
OpCode::Eq => self.binary_op(|a, b| Ok(Value::Boolean(a == b)))?,
OpCode::Neq => self.binary_op(|a, b| Ok(Value::Boolean(a != b)))?,
OpCode::Lt => self.binary_op(|a, b| {
match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Boolean(a < b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Boolean(a < b)),
(Value::Integer(a), Value::Float(b)) => Ok(Value::Boolean((a as f64) < b)),
(Value::Float(a), Value::Integer(b)) => Ok(Value::Boolean(a < (b as f64))),
_ => Err("Invalid types for LT".into()),
}
})?,
OpCode::Gt => self.binary_op(|a, b| {
match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Boolean(a > b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Boolean(a > b)),
(Value::Integer(a), Value::Float(b)) => Ok(Value::Boolean((a as f64) > b)),
(Value::Float(a), Value::Integer(b)) => Ok(Value::Boolean(a > (b as f64))),
_ => Err("Invalid types for GT".into()),
}
})?,
OpCode::And => self.binary_op(|a, b| match (a, b) {
(Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(a && b)),
_ => Err("Invalid types for AND".into()),
})?,
OpCode::Or => self.binary_op(|a, b| match (a, b) {
(Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(a || b)),
_ => Err("Invalid types for OR".into()),
})?,
OpCode::Not => {
let val = self.pop()?;
if let Value::Boolean(b) = val {
self.push(Value::Boolean(!b));
} else {
return Err("Invalid type for NOT".into());
}
}
OpCode::GetGlobal => {
let idx = self.read_u32()? as usize;
let val = self.globals.get(idx).cloned().ok_or("Invalid global index")?;
self.push(val);
}
OpCode::SetGlobal => {
let idx = self.read_u32()? as usize;
let val = self.pop()?;
if idx >= self.globals.len() {
self.globals.resize(idx + 1, Value::Null);
}
self.globals[idx] = val;
}
OpCode::GetLocal => {
let idx = self.read_u32()? as usize;
let frame = self.call_stack.last().ok_or("No active call frame")?;
let val = self.operand_stack.get(frame.stack_base + idx).cloned().ok_or("Invalid local index")?;
self.push(val);
}
OpCode::SetLocal => {
let idx = self.read_u32()? as usize;
let val = self.pop()?;
let frame = self.call_stack.last().ok_or("No active call frame")?;
let stack_idx = frame.stack_base + idx;
if stack_idx >= self.operand_stack.len() {
return Err("Local index out of bounds".into());
}
self.operand_stack[stack_idx] = val;
}
OpCode::Call => {
let addr = self.read_u32()? as usize;
let args_count = self.read_u32()? as usize;
let stack_base = self.operand_stack.len() - args_count;
self.call_stack.push(CallFrame {
return_address: self.pc,
stack_base,
locals_count: args_count,
});
self.pc = addr;
}
OpCode::Ret => {
let frame = self.call_stack.pop().ok_or("Call stack underflow")?;
let return_val = self.pop()?;
self.operand_stack.truncate(frame.stack_base);
self.push(return_val);
self.pc = frame.return_address;
}
OpCode::PushScope => {
let locals_count = self.read_u32()? as usize;
let stack_base = self.operand_stack.len();
for _ in 0..locals_count {
self.push(Value::Null);
}
self.call_stack.push(CallFrame {
return_address: 0,
stack_base,
locals_count,
});
}
OpCode::PopScope => {
let frame = self.call_stack.pop().ok_or("Call stack underflow")?;
self.operand_stack.truncate(frame.stack_base);
}
OpCode::Alloc => {
let size = self.read_u32()? as usize;
let ref_idx = self.heap.len();
for _ in 0..size {
self.heap.push(Value::Null);
}
self.push(Value::Ref(ref_idx));
}
OpCode::LoadRef => {
let offset = self.read_u32()? as usize;
let ref_val = self.pop()?;
if let Value::Ref(base) = ref_val {
let val = self.heap.get(base + offset).cloned().ok_or("Invalid heap access")?;
self.push(val);
} else {
return Err("Expected reference for LOAD_REF".into());
}
}
OpCode::StoreRef => {
let offset = self.read_u32()? as usize;
let val = self.pop()?;
let ref_val = self.pop()?;
if let Value::Ref(base) = ref_val {
if base + offset >= self.heap.len() {
return Err("Invalid heap access".into());
}
self.heap[base + offset] = val;
} else {
return Err("Expected reference for STORE_REF".into());
}
}
OpCode::Syscall => {
let id = self.read_u32()?;
let native_cycles = native.syscall(id, self, hw).map_err(|e| format!("syscall 0x{:08X} failed: {}", id, e))?;
self.cycles += native_cycles;
}
OpCode::FrameSync => {
return Ok(());
}
}
self.cycles += opcode.cycles();
Ok(())
}
fn read_u32(&mut self) -> Result<u32, String> {
if self.pc + 4 > self.program.rom.len() {
return Err("Unexpected end of ROM".into());
}
let bytes = [
self.program.rom[self.pc],
self.program.rom[self.pc + 1],
self.program.rom[self.pc + 2],
self.program.rom[self.pc + 3],
];
self.pc += 4;
Ok(u32::from_le_bytes(bytes))
}
fn read_u16(&mut self) -> Result<u16, String> {
if self.pc + 2 > self.program.rom.len() {
return Err("Unexpected end of ROM".into());
}
let bytes = [
self.program.rom[self.pc],
self.program.rom[self.pc + 1],
];
self.pc += 2;
Ok(u16::from_le_bytes(bytes))
}
pub fn push(&mut self, val: Value) {
self.operand_stack.push(val);
}
pub fn pop(&mut self) -> Result<Value, String> {
self.operand_stack.pop().ok_or("Stack underflow".into())
}
pub fn pop_number(&mut self) -> Result<f64, String> {
let val = self.pop()?;
val.as_float().ok_or_else(|| "Expected number".into())
}
pub fn pop_integer(&mut self) -> Result<i64, String> {
let val = self.pop()?;
val.as_integer().ok_or_else(|| "Expected integer".into())
}
pub fn peek(&self) -> Result<&Value, String> {
self.operand_stack.last().ok_or("Stack underflow".into())
}
fn binary_op<F>(&mut self, f: F) -> Result<(), String>
where
F: FnOnce(Value, Value) -> Result<Value, String>,
{
let b = self.pop()?;
let a = self.pop()?;
let res = f(a, b)?;
self.push(res);
Ok(())
}
}