|  |  |  | @ -1,5 +1,5 @@ | 
			
		
	
		
			
				
					|  |  |  |  | use std::any::{Any, TypeId}; | 
			
		
	
		
			
				
					|  |  |  |  | use std::collections::HashMap; | 
			
		
	
		
			
				
					|  |  |  |  | use std::collections::{HashMap, VecDeque}; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | use crate::asm::data::{Rd, RdData, RdObj, Register, Wr, WrData}; | 
			
		
	
		
			
				
					|  |  |  |  | use crate::asm::data::literal::{Addr, Value}; | 
			
		
	
	
		
			
				
					|  |  |  | @ -12,34 +12,95 @@ use nudge::{likely}; | 
			
		
	
		
			
				
					|  |  |  |  | use crate::asm::instr::cond::Flag; | 
			
		
	
		
			
				
					|  |  |  |  | use std::fmt::{Debug, Formatter}; | 
			
		
	
		
			
				
					|  |  |  |  | use std::fmt; | 
			
		
	
		
			
				
					|  |  |  |  | use std::time::Instant; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | pub struct RunState { | 
			
		
	
		
			
				
					|  |  |  |  |     pub thread_info: Arc<ThreadInfo>, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Active stack frame
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub frame: StackFrame, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Call stack
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub call_stack: CallStack, | 
			
		
	
		
			
				
					|  |  |  |  |     /// The active coroutine
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub cr: CoroutineContext, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Parked coroutines
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub parked: VecDeque<CoroutineContext>, | 
			
		
	
		
			
				
					|  |  |  |  |     /// 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, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Execution deadline, if multi-tasked
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub cr_deadline: Option<Instant>, | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #[derive(Debug,Default)] | 
			
		
	
		
			
				
					|  |  |  |  | pub struct CoroutineContext { | 
			
		
	
		
			
				
					|  |  |  |  |     pub handle: Value, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Active stack frame
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub frame: StackFrame, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Call stack
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub call_stack: CallStack, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Coroutine run state
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub cr_state: CoroutineState, | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | /// Execution state of an inactive coroutine
 | 
			
		
	
		
			
				
					|  |  |  |  | #[derive(Debug, Eq, PartialEq)] | 
			
		
	
		
			
				
					|  |  |  |  | pub enum CoroutineState { | 
			
		
	
		
			
				
					|  |  |  |  |     /// Ready to run, just started, or interrupted by the scheduler
 | 
			
		
	
		
			
				
					|  |  |  |  |     Ready, | 
			
		
	
		
			
				
					|  |  |  |  |     /// Delay in progress
 | 
			
		
	
		
			
				
					|  |  |  |  |     Sleep { due: Instant }, | 
			
		
	
		
			
				
					|  |  |  |  |     /// The task finished
 | 
			
		
	
		
			
				
					|  |  |  |  |     Finished(Vec<Value>), | 
			
		
	
		
			
				
					|  |  |  |  |     /// The task yielded a value that must be consumed before it can resume.
 | 
			
		
	
		
			
				
					|  |  |  |  |     /// State switches to Ready when the value is read.
 | 
			
		
	
		
			
				
					|  |  |  |  |     YieldedValue(Value), | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | impl CoroutineState { | 
			
		
	
		
			
				
					|  |  |  |  |     pub fn is_alive(&self) -> bool { | 
			
		
	
		
			
				
					|  |  |  |  |         match self { | 
			
		
	
		
			
				
					|  |  |  |  |             CoroutineState::Ready => true, | 
			
		
	
		
			
				
					|  |  |  |  |             CoroutineState::Sleep { .. } => true, | 
			
		
	
		
			
				
					|  |  |  |  |             CoroutineState::Finished(_) => false, | 
			
		
	
		
			
				
					|  |  |  |  |             CoroutineState::YieldedValue(_) => true, | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | impl Default for CoroutineState { | 
			
		
	
		
			
				
					|  |  |  |  |     fn default() -> Self { | 
			
		
	
		
			
				
					|  |  |  |  |         Self::Ready | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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.cr = Default::default(); | 
			
		
	
		
			
				
					|  |  |  |  |         self.parked = Default::default(); | 
			
		
	
		
			
				
					|  |  |  |  |         self.global_regs = Default::default(); | 
			
		
	
		
			
				
					|  |  |  |  |         self.ext_data = Default::default(); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /// Add a coroutine, marked as Ready, to run next
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub fn add_coroutine(&mut self, frame : StackFrame) -> Value { | 
			
		
	
		
			
				
					|  |  |  |  |         let handle = self.thread_info.unique_handle(); | 
			
		
	
		
			
				
					|  |  |  |  |         self.parked.push_back(CoroutineContext { | 
			
		
	
		
			
				
					|  |  |  |  |             handle, | 
			
		
	
		
			
				
					|  |  |  |  |             frame, | 
			
		
	
		
			
				
					|  |  |  |  |             call_stack: vec![], | 
			
		
	
		
			
				
					|  |  |  |  |             cr_state: Default::default() | 
			
		
	
		
			
				
					|  |  |  |  |         }); | 
			
		
	
		
			
				
					|  |  |  |  |         if self.cr_deadline.is_none() { | 
			
		
	
		
			
				
					|  |  |  |  |             // start context switching
 | 
			
		
	
		
			
				
					|  |  |  |  |             self.cr_deadline = Some(Instant::now() + self.thread_info.scheduler_interval); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         handle | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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("cr", &self.cr) | 
			
		
	
		
			
				
					|  |  |  |  |             .field("parked", &self.parked) | 
			
		
	
		
			
				
					|  |  |  |  |             .field("global_regs", &self.global_regs) | 
			
		
	
		
			
				
					|  |  |  |  |             //.field("ext_data", &self.ext_data)
 | 
			
		
	
		
			
				
					|  |  |  |  |             .finish() | 
			
		
	
	
		
			
				
					|  |  |  | @ -79,44 +140,44 @@ impl RunState { | 
			
		
	
		
			
				
					|  |  |  |  |     #[inline(always)] | 
			
		
	
		
			
				
					|  |  |  |  |     pub fn set_flag(&mut self, flag: Flag, set: bool) { | 
			
		
	
		
			
				
					|  |  |  |  |         trace!("Flag {} = {:?}", flag, set); | 
			
		
	
		
			
				
					|  |  |  |  |         self.frame.status.set(flag, set); | 
			
		
	
		
			
				
					|  |  |  |  |         self.cr.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) | 
			
		
	
		
			
				
					|  |  |  |  |         self.cr.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; | 
			
		
	
		
			
				
					|  |  |  |  |         self.cr.frame.pc = pc; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /// Get program counter - address of the next instruction to run
 | 
			
		
	
		
			
				
					|  |  |  |  |     pub fn get_pc(&self) -> Addr { | 
			
		
	
		
			
				
					|  |  |  |  |         self.frame.pc | 
			
		
	
		
			
				
					|  |  |  |  |         self.cr.frame.pc | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /// Clear status flags
 | 
			
		
	
		
			
				
					|  |  |  |  |     #[inline(always)] | 
			
		
	
		
			
				
					|  |  |  |  |     pub fn clear_status(&mut self) { | 
			
		
	
		
			
				
					|  |  |  |  |         self.frame.status.clear(); | 
			
		
	
		
			
				
					|  |  |  |  |         self.cr.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); | 
			
		
	
		
			
				
					|  |  |  |  |         self.cr.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); | 
			
		
	
		
			
				
					|  |  |  |  |         self.cr.frame.status.update_float(val); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /// Read object handle value
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -133,8 +194,8 @@ impl RunState { | 
			
		
	
		
			
				
					|  |  |  |  |             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]) | 
			
		
	
		
			
				
					|  |  |  |  |                     trace!("Rd {:?} = {}", rd, self.cr.frame.gen[rn as usize]); | 
			
		
	
		
			
				
					|  |  |  |  |                     Ok(self.cr.frame.gen[rn as usize]) | 
			
		
	
		
			
				
					|  |  |  |  |                 } else { | 
			
		
	
		
			
				
					|  |  |  |  |                     Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
	
		
			
				
					|  |  |  | @ -149,16 +210,16 @@ impl RunState { | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             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]) | 
			
		
	
		
			
				
					|  |  |  |  |                     trace!("Rd {:?} = {}", rd, self.cr.frame.arg[rn as usize]); | 
			
		
	
		
			
				
					|  |  |  |  |                     Ok(self.cr.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]) | 
			
		
	
		
			
				
					|  |  |  |  |                     trace!("Rd {:?} = {}", rd, self.cr.frame.res[rn as usize]); | 
			
		
	
		
			
				
					|  |  |  |  |                     Ok(self.cr.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
 | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
	
		
			
				
					|  |  |  | @ -174,6 +235,26 @@ impl RunState { | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     fn read_object(&mut self, reference: Value) -> Result<Value, Fault> { | 
			
		
	
		
			
				
					|  |  |  |  |         // values yielded from coroutines
 | 
			
		
	
		
			
				
					|  |  |  |  |         for cr in &mut self.parked { | 
			
		
	
		
			
				
					|  |  |  |  |             if cr.handle == reference { | 
			
		
	
		
			
				
					|  |  |  |  |                 match cr.cr_state { | 
			
		
	
		
			
				
					|  |  |  |  |                     CoroutineState::Ready | CoroutineState::Sleep { .. } => { | 
			
		
	
		
			
				
					|  |  |  |  |                         return Err(Fault::Blocked); | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                     CoroutineState::Finished(_) => { | 
			
		
	
		
			
				
					|  |  |  |  |                         self.set_flag(Flag::Eof, true); | 
			
		
	
		
			
				
					|  |  |  |  |                         warn!("Attempt to read yielded value of a finished coroutine"); | 
			
		
	
		
			
				
					|  |  |  |  |                         return Ok(0); | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                     CoroutineState::YieldedValue(v) => { | 
			
		
	
		
			
				
					|  |  |  |  |                         cr.cr_state = CoroutineState::Ready; | 
			
		
	
		
			
				
					|  |  |  |  |                         return Ok(v); | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 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.
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -213,7 +294,7 @@ impl RunState { | 
			
		
	
		
			
				
					|  |  |  |  |         match wr.0 { | 
			
		
	
		
			
				
					|  |  |  |  |             WrData::Register(Register::Gen(rn)) => { | 
			
		
	
		
			
				
					|  |  |  |  |                 if likely(rn < REG_COUNT as u8) { | 
			
		
	
		
			
				
					|  |  |  |  |                     self.frame.gen[rn as usize] = val; | 
			
		
	
		
			
				
					|  |  |  |  |                     self.cr.frame.gen[rn as usize] = val; | 
			
		
	
		
			
				
					|  |  |  |  |                     Ok(()) | 
			
		
	
		
			
				
					|  |  |  |  |                 } else { | 
			
		
	
		
			
				
					|  |  |  |  |                     Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) | 
			
		
	
	
		
			
				
					|  |  |  | 
 |