use std::any::{Any, TypeId}; use std::collections::HashMap; use crate::asm::data::{Rd, RdData, RdObj, Register, Wr, WrData}; use crate::asm::data::literal::{Addr, Value}; use crate::asm::instr::Cond; use crate::runtime::fault::Fault; use crate::runtime::frame::{CallStack, REG_COUNT, StackFrame}; use std::sync::Arc; use crate::runtime::run_thread::ThreadInfo; use nudge::{likely}; use crate::asm::instr::cond::Flag; use std::fmt::{Debug, Formatter}; use std::fmt; pub struct RunState { pub thread_info: Arc, /// Active stack frame pub frame: StackFrame, /// Call stack pub call_stack: CallStack, /// General purpose registers that stay valid for the entire run-time of the thread pub global_regs: [Value; REG_COUNT], /// Extension data pub ext_data: ExtensionDataStore, } impl RunState { /// Clear everything. Caution: when done at runtime, this effectively reboots the thread pub fn clear_all(&mut self) { self.frame = Default::default(); self.call_stack = Default::default(); self.global_regs = Default::default(); self.ext_data = Default::default(); } } impl Debug for RunState { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("RunState") .field("frame", &self.frame) .field("call_stack", &self.call_stack) .field("global_regs", &self.global_regs) //.field("ext_data", &self.ext_data) .finish() } } #[derive(Debug,Default)] pub struct ExtensionDataStore { store: HashMap> } impl ExtensionDataStore { pub fn new() -> Self { Default::default() } /// Get an extension's persistent data object for mutation. /// It will be created using Default if not present before. pub fn get_mut(&mut self) -> &mut T { if !self.store.contains_key(&TypeId::of::()) { self.store.insert(TypeId::of::(), Box::new(T::default())); } self.store.get_mut(&TypeId::of::()).unwrap() .downcast_mut().unwrap() } } impl RunState { /// Get an extension's persistent data object for mutation. /// It will be created using Default if not present before. pub fn ext_mut(&mut self) -> &mut T { self.ext_data.get_mut() } /// Set a status flag. Only supports simple, positive conds (i.e. not GreaterOrEqual) #[inline(always)] pub fn set_flag(&mut self, flag: Flag, set: bool) { trace!("Flag {} = {:?}", flag, set); self.frame.status.set(flag, set); } /// Check status flags for a condition #[inline(always)] pub fn test_cond(&self, cond: Cond) -> bool { self.frame.status.test(cond) } /// Set program counter - address of the next instruction to run pub fn set_pc(&mut self, pc: Addr) { trace!("PC := {}", pc); self.frame.pc = pc; } /// Get program counter - address of the next instruction to run pub fn get_pc(&self) -> Addr { self.frame.pc } /// Clear status flags #[inline(always)] pub fn clear_status(&mut self) { self.frame.status.clear(); } /// Update status flags using a variable. /// The update is additive - call `clear_status()` first if desired! #[inline(always)] pub fn update_status(&mut self, val: Value) { self.frame.status.update(val); } /// Update status flags using a variable. /// The update is additive - call `clear_status()` first if desired! #[inline(always)] pub fn update_status_float(&mut self, val: f64) { self.frame.status.update_float(val); } /// Read object handle value pub fn read_obj(&mut self, rdo: impl Into) -> Result { rdo.into().read(self) } /// Read a `Rd` value #[inline] pub fn read(&mut self, rd: impl Into) -> Result { let rd = rd.into(); // TODO dedupe match rd.0 { RdData::Immediate(v) => Ok(v), RdData::Register(Register::Gen(rn)) => { if likely(rn < REG_COUNT as u8) { trace!("Rd {:?} = {}", rd, self.frame.gen[rn as usize]); Ok(self.frame.gen[rn as usize]) } else { Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) } } RdData::Register(Register::Global(rn)) => { if likely(rn < REG_COUNT as u8) { trace!("Rd {:?} = {}", rd, self.global_regs[rn as usize]); Ok(self.global_regs[rn as usize]) } else { Err(Fault::RegisterNotExist { reg: Register::Global(rn) }) } } RdData::Register(Register::Arg(rn)) => { if likely(rn < REG_COUNT as u8) { trace!("Rd {:?} = {}", rd, self.frame.arg[rn as usize]); Ok(self.frame.arg[rn as usize]) } else { Err(Fault::RegisterNotExist { reg: Register::Arg(rn) }) } } RdData::Register(Register::Res(rn)) => { if likely(rn < REG_COUNT as u8) { trace!("Rd {:?} = {}", rd, self.frame.res[rn as usize]); Ok(self.frame.res[rn as usize]) } else { Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490 } } RdData::RegObject(register) => { let reference = self.read(Rd::new(RdData::Register(register)))?; self.read_object(reference) } RdData::ImmObject(reference) => { self.read_object(reference) } } } fn read_object(&mut self, reference: Value) -> Result { // This is a shitty dirty hack to allow iterating over the extensions while passing a mutable reference // to self to the reading methods. Since the extensions array is in an Arc, it can't be mutated internally // anyway, and we know it will still live after the method returns - unless someone does something incredibly stupid. let ti: &ThreadInfo = unsafe { &*(self.thread_info.as_ref() as *const _) }; // If this fails, just clone the arc for the loop... for ext in ti.extensions.iter() { if let Some(value) = ext.read_obj(self, reference)? { return Ok(value); } } Err(Fault::ObjectNotExist(reference)) } fn write_object(&mut self, reference: Value, value: Value) -> Result<(), Fault> { // This is a shitty dirty hack to allow iterating over the extensions while passing a mutable reference // to self to the reading methods. Since the extensions array is in an Arc, it can't be mutated internally // anyway, and we know it will still live after the method returns - unless someone does something incredibly stupid. let ti: &ThreadInfo = unsafe { &*(self.thread_info.as_ref() as *const _) }; // If this fails, just clone the arc for the loop... for ext in ti.extensions.iter() { if ext.write_obj(self, reference, value)?.is_some() { return Ok(()); } } Err(Fault::ObjectNotExist(reference)) } /// Write a value to a `Wr` location pub fn write(&mut self, wr: impl Into, val: Value) -> Result<(), Fault> { let wr = wr.into(); trace!("WR {:?} := {}", wr, val); // TODO dedupe match wr.0 { WrData::Register(Register::Gen(rn)) => { if likely(rn < REG_COUNT as u8) { self.frame.gen[rn as usize] = val; Ok(()) } else { Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) } } WrData::Register(Register::Global(rn)) => { if likely(rn < REG_COUNT as u8) { self.global_regs[rn as usize] = val; Ok(()) } else { Err(Fault::RegisterNotExist { reg: Register::Global(rn) }) } } WrData::Register(Register::Arg(rn)) => { if likely(rn < REG_COUNT as u8) { Err(Fault::RegisterNotWritable { reg: Register::Res(rn) }) } else { Err(Fault::RegisterNotExist { reg: Register::Arg(rn) }) } } WrData::Register(Register::Res(rn)) => { if likely(rn < REG_COUNT as u8) { Err(Fault::RegisterNotWritable { reg: Register::Res(rn) }) } else { Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490 } } WrData::Discard => { /* Discard */ Ok(()) } WrData::RegObject(register) => { let reference = self.read(Rd::new(RdData::Register(register)))?; self.write_object(reference, val) } WrData::ImmObject(reference) => { self.write_object(reference, val) } } } }