|
|
|
@ -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) }) |
|
|
|
|