parent
def2fc8db0
commit
5f4fd0e806
@ -1,5 +1,5 @@ |
||||
[package] |
||||
name = "csn_asm" |
||||
name = "asm" |
||||
version = "0.1.0" |
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||
edition = "2018" |
@ -0,0 +1,188 @@ |
||||
use super::error::AsmError; |
||||
|
||||
|
||||
pub mod literal; |
||||
pub mod reg; |
||||
pub mod mask; |
||||
|
||||
pub use reg::Register; |
||||
pub use mask::Mask; |
||||
use literal::Addr; |
||||
|
||||
use std::convert::TryFrom; |
||||
|
||||
use crate::data::literal::{Value, is_negative, as_signed}; |
||||
use std::fmt::{Debug, Formatter, Display}; |
||||
use std::fmt; |
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum DataDisp { |
||||
/// Constant value
|
||||
Immediate(Value), |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
impl Display for DataDisp { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
match self { |
||||
DataDisp::Immediate(v) => { |
||||
write!(f, "{}", as_signed(*v)) |
||||
} |
||||
DataDisp::ImmediatePtr(a) => { |
||||
write!(f, "@0x{:#18x}", a.0) |
||||
} |
||||
DataDisp::Register(r) => { |
||||
write!(f, "{}", r) |
||||
} |
||||
DataDisp::RegisterPtr(r) => { |
||||
write!(f, "@{}", r) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl From<SrcDisp> for DataDisp { |
||||
fn from(s: SrcDisp) -> Self { |
||||
match s { |
||||
SrcDisp::Immediate(x) => DataDisp::Immediate(x), |
||||
SrcDisp::ImmediatePtr(x) => DataDisp::ImmediatePtr(x), |
||||
SrcDisp::Register(x) => DataDisp::Register(x), |
||||
SrcDisp::RegisterPtr(x) => DataDisp::RegisterPtr(x), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl From<DstDisp> for DataDisp { |
||||
fn from(s: DstDisp) -> Self { |
||||
match s { |
||||
DstDisp::ImmediatePtr(x) => DataDisp::ImmediatePtr(x), |
||||
DstDisp::Register(x) => DataDisp::Register(x), |
||||
DstDisp::RegisterPtr(x) => DataDisp::RegisterPtr(x), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum SrcDisp { |
||||
/// Constant value
|
||||
Immediate(Value), |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
impl TryFrom<DataDisp> for SrcDisp { |
||||
type Error = AsmError; |
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> { |
||||
match value { |
||||
DataDisp::Immediate(x) => Ok(SrcDisp::Immediate(x)), |
||||
DataDisp::ImmediatePtr(x) => Ok(SrcDisp::ImmediatePtr(x)), |
||||
DataDisp::Register(x) => Ok(SrcDisp::Register(x)), |
||||
DataDisp::RegisterPtr(x) => Ok(SrcDisp::RegisterPtr(x)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Data destination disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum DstDisp { |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
impl From<DstDisp> for SrcDisp { |
||||
fn from(s: DstDisp) -> Self { |
||||
match s { |
||||
DstDisp::ImmediatePtr(x) => SrcDisp::ImmediatePtr(x), |
||||
DstDisp::Register(x) => SrcDisp::Register(x), |
||||
DstDisp::RegisterPtr(x) => SrcDisp::RegisterPtr(x), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl TryFrom<DataDisp> for DstDisp { |
||||
type Error = AsmError; |
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> { |
||||
match value { |
||||
DataDisp::Immediate(_x) => Err(AsmError::ValueAsOutput), |
||||
DataDisp::ImmediatePtr(x) => Ok(DstDisp::ImmediatePtr(x)), |
||||
DataDisp::Register(x) => Ok(DstDisp::Register(x)), |
||||
DataDisp::RegisterPtr(x) => Ok(DstDisp::RegisterPtr(x)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Data source argument (read-only)
|
||||
#[derive(Clone, Copy, Eq, PartialEq)] |
||||
pub struct Rd(SrcDisp, Mask); |
||||
|
||||
impl Rd { |
||||
pub fn new(src: SrcDisp) -> Self { |
||||
Rd(src, Mask::default()) |
||||
} |
||||
pub fn d(self) -> SrcDisp { |
||||
self.0 |
||||
} |
||||
pub fn mask(self) -> Mask { |
||||
self.1 |
||||
} |
||||
} |
||||
|
||||
/// Data destination argument (read-write)
|
||||
#[derive(Clone, Copy, Eq, PartialEq)] |
||||
pub struct Wr(DstDisp, Mask); |
||||
|
||||
impl Wr { |
||||
pub fn new(dst: DstDisp) -> Self { |
||||
Wr(dst, Mask::default()) |
||||
} |
||||
pub fn d(self) -> DstDisp { |
||||
self.0 |
||||
} |
||||
pub fn mask(self) -> Mask { |
||||
self.1 |
||||
} |
||||
pub fn as_rd(&self) -> Rd { |
||||
Rd(self.0.into(), self.1) |
||||
} |
||||
} |
||||
|
||||
impl Debug for Rd { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
write!(f, "Rd(")?; |
||||
let disp : DataDisp = self.0.into(); |
||||
write!(f, "{}", disp); |
||||
if !self.mask().is_default() { |
||||
write!(f, ",{:?}", self.mask()); |
||||
} |
||||
write!(f, ")") |
||||
} |
||||
} |
||||
|
||||
impl Debug for Wr { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
write!(f, "Wr(")?; |
||||
let disp : DataDisp = self.0.into(); |
||||
write!(f, "{}", disp); |
||||
if !self.mask().is_default() { |
||||
write!(f, ",{:?}", self.mask()); |
||||
} |
||||
write!(f, ")") |
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
[package] |
||||
name = "crsn" |
||||
version = "0.1.0" |
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
asm = { path = "../asm" } |
||||
runtime = { path = "../runtime" } |
||||
simple_logger = "1.9.0" |
||||
log = "0.4.11" |
||||
thiserror = "1.0.20" |
||||
anyhow = "1.0.32" |
@ -0,0 +1,36 @@ |
||||
#[macro_use] |
||||
extern crate log; |
||||
|
||||
use simple_logger::SimpleLogger; |
||||
use runtime::run_thread::{RunThread, ThreadToken}; |
||||
use runtime::program::Program; |
||||
use asm::data::literal::Addr; |
||||
|
||||
fn main() { |
||||
SimpleLogger::new().init().unwrap(); |
||||
|
||||
// ;(dec r0 (z? (ret)))
|
||||
let program = " |
||||
( |
||||
(main |
||||
(ld r0 2) |
||||
(:again) |
||||
(dec r0) |
||||
(jif nz :again) |
||||
(fault \"that's it\") |
||||
) |
||||
) |
||||
"; |
||||
|
||||
let parsed = asm::assemble(program).unwrap(); |
||||
|
||||
debug!("---"); |
||||
for op in &parsed { |
||||
debug!("{:?}", op); |
||||
} |
||||
debug!("---"); |
||||
|
||||
let thread = RunThread::new(ThreadToken(0), Program::new(parsed), Addr(0), &[]); |
||||
|
||||
thread.start().join().unwrap(); |
||||
} |
@ -1,97 +0,0 @@ |
||||
use super::error::AsmError; |
||||
|
||||
|
||||
pub mod literal; |
||||
pub mod reg; |
||||
pub mod mask; |
||||
|
||||
pub use reg::Register; |
||||
pub use mask::Mask; |
||||
use literal::Addr; |
||||
|
||||
use std::convert::TryFrom; |
||||
|
||||
use crate::data::literal::Value; |
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum DataDisp { |
||||
/// Constant value
|
||||
Immediate(Value), |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum SrcDisp { |
||||
/// Constant value
|
||||
Immediate(Value), |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
impl TryFrom<DataDisp> for SrcDisp { |
||||
type Error = AsmError; |
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> { |
||||
match value { |
||||
DataDisp::Immediate(x) => Ok(SrcDisp::Immediate(x)), |
||||
DataDisp::ImmediatePtr(x) => Ok(SrcDisp::ImmediatePtr(x)), |
||||
DataDisp::Register(x) => Ok(SrcDisp::Register(x)), |
||||
DataDisp::RegisterPtr(x) => Ok(SrcDisp::RegisterPtr(x)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Data destination disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum DstDisp { |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
impl TryFrom<DataDisp> for DstDisp { |
||||
type Error = AsmError; |
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> { |
||||
match value { |
||||
DataDisp::Immediate(_x) => Err(AsmError::ValueAsOutput), |
||||
DataDisp::ImmediatePtr(x) => Ok(DstDisp::ImmediatePtr(x)), |
||||
DataDisp::Register(x) => Ok(DstDisp::Register(x)), |
||||
DataDisp::RegisterPtr(x) => Ok(DstDisp::RegisterPtr(x)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Data source argument (read-only)
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub struct Rd(SrcDisp, Mask); |
||||
|
||||
impl Rd { |
||||
pub fn new(src : SrcDisp) -> Self { |
||||
Rd(src, Mask::default()) |
||||
} |
||||
} |
||||
|
||||
/// Data destination argument (read-write)
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub struct Wr(DstDisp, Mask); |
||||
|
||||
impl Wr { |
||||
pub fn new(dst : DstDisp) -> Self { |
||||
Wr(dst, Mask::default()) |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
[package] |
||||
name = "runtime" |
||||
version = "0.1.0" |
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
asm = { path = "../asm" } |
||||
thiserror = "1.0.20" |
||||
anyhow = "1.0.32" |
||||
log = "0.4.11" |
@ -0,0 +1,98 @@ |
||||
use crate::run_thread::{ThreadToken, RunThread}; |
||||
use asm::instr::{Op, Cond}; |
||||
use crate::fault::Fault; |
||||
use crate::frame::StackFrame; |
||||
use asm::data::literal::{Value, is_positive, is_negative}; |
||||
|
||||
pub type CyclesSpent = usize; |
||||
|
||||
pub struct EvalRes { |
||||
pub cycles: u8, |
||||
pub advance: i64, |
||||
} |
||||
|
||||
impl RunThread { |
||||
pub fn eval_op(&mut self) -> Result<EvalRes, Fault> { |
||||
let mut cycles = 1; |
||||
let mut advance = 1; |
||||
|
||||
let mut frame = &mut self.frame; |
||||
|
||||
let op = self.program.read(frame.pc); |
||||
debug!("{} | {:?}", frame.pc, op); |
||||
|
||||
/* Operations can be given different execution times when run in slow mode. */ |
||||
/* Presently, all that do anything use 1 cycle. */ |
||||
|
||||
match op { |
||||
Op::Nop => {} |
||||
Op::FarLabel(_) | Op::Routine(_) => { |
||||
/* this is nop, but without any cost - just markers */ |
||||
cycles = 0; |
||||
} |
||||
Op::Barrier(msg) => { |
||||
return Err(Fault::Barrier { |
||||
msg: msg.clone().unwrap_or_else(|| "No msg".into()) |
||||
}) |
||||
} |
||||
Op::Fault(msg) => { |
||||
return Err(Fault::FaultInstr { |
||||
msg: msg.clone().unwrap_or_else(|| "No msg".into()) |
||||
}) |
||||
} |
||||
Op::FarJump(_) => unimplemented!(), |
||||
Op::Call(_, _) => unimplemented!(), |
||||
Op::Ret(_) => unimplemented!(), |
||||
Op::Skip(val) => { |
||||
let steps = frame.read(*val)?; |
||||
advance = i64::from_ne_bytes(steps.to_ne_bytes()); |
||||
} |
||||
Op::SkipIf(cond, val) => { |
||||
if frame.status.test(*cond) { |
||||
let steps = frame.read(*val)?; |
||||
advance = i64::from_ne_bytes(steps.to_ne_bytes()); |
||||
} |
||||
} |
||||
Op::Mov(dst, src) => { |
||||
let val = frame.read(*src)?; |
||||
frame.write(*dst, val)?; |
||||
} |
||||
Op::Cmp(a, b) => { |
||||
frame.status.clear(); |
||||
let a = frame.read(*a)?; |
||||
let b = frame.read(*b)?; |
||||
frame.status.equal = a == b; |
||||
frame.status.zero = a == 0 && b == 0; |
||||
frame.status.lower = a < b; |
||||
frame.status.greater = a > b; |
||||
frame.status.positive = is_positive(a) && is_positive(b); |
||||
frame.status.negative = is_negative(a) && is_negative(b); |
||||
} |
||||
Op::Inc(reg) => { |
||||
frame.status.clear(); |
||||
let mut val = frame.read(reg.as_rd())?; |
||||
val = val.wrapping_add(1); |
||||
frame.status.overflow = (val == 0); |
||||
frame.status.zero = (val == 0); |
||||
frame.status.positive = is_positive(val); |
||||
frame.status.negative = is_negative(val); |
||||
frame.write(*reg, val)?; |
||||
} |
||||
Op::Dec(reg) => { |
||||
frame.status.clear(); |
||||
let mut val = frame.read(reg.as_rd())?; |
||||
frame.status.overflow = (val == 0); // will overflow
|
||||
val = val.wrapping_sub(1); |
||||
frame.status.zero = (val == 0); |
||||
frame.status.positive = is_positive(val); |
||||
frame.status.negative = is_negative(val); |
||||
frame.write(*reg, val)?; |
||||
} |
||||
} |
||||
|
||||
Ok(EvalRes { |
||||
cycles, |
||||
advance, |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,59 @@ |
||||
use thiserror::Error; |
||||
use super::span::MemorySpan; |
||||
use crate::run_thread::ThreadToken; |
||||
use crate::mlock::ClaimId; |
||||
use asm::data::literal::DebugMsg; |
||||
use asm::data::Register; |
||||
|
||||
#[derive(Error,Debug)] |
||||
pub enum Fault { |
||||
#[error("Bad instruction at addr {addr:#10x}: {cause}")] |
||||
BadInstruction { |
||||
addr : u32, |
||||
cause: InstrError, |
||||
}, |
||||
|
||||
#[error("Runtime hit a barrier instruction: {msg}")] |
||||
Barrier { |
||||
msg: DebugMsg, |
||||
}, |
||||
|
||||
#[error("User fault: {msg}")] |
||||
FaultInstr { |
||||
msg: DebugMsg, |
||||
}, |
||||
|
||||
#[error("Memory region {area:?} is locked by thread {owner:?}")] |
||||
MemoryLocked { |
||||
area: MemorySpan, |
||||
owner: ThreadToken |
||||
}, |
||||
|
||||
#[error("Memory claim {claim:?} owned by thread {owner:?} does not exist")] |
||||
ClaimNotExist { |
||||
claim: ClaimId, |
||||
owner: ThreadToken |
||||
}, |
||||
|
||||
#[error("Register does not exist: {reg:?}")] |
||||
RegisterNotExist { |
||||
reg: Register, |
||||
}, |
||||
|
||||
#[error("Register is read-only: {reg:?}")] |
||||
RegisterNotWritable { |
||||
reg: Register, |
||||
}, |
||||
} |
||||
|
||||
#[derive(Error,Debug)] |
||||
pub enum InstrError { |
||||
#[error("Instruction not recognized")] |
||||
UnknownInstruction, |
||||
|
||||
#[error("Invalid bit span")] |
||||
BadBitSpan, |
||||
|
||||
#[error("Operands data size differs")] |
||||
UnevenOperandSize, |
||||
} |
@ -0,0 +1,150 @@ |
||||
use asm::data::literal::{Addr, Value}; |
||||
use asm::data::{Rd, SrcDisp, Register, Wr, DstDisp}; |
||||
use crate::fault::Fault; |
||||
use asm::instr::Cond; |
||||
|
||||
pub const REG_COUNT: usize = 8; |
||||
|
||||
#[derive(Default, Clone, Debug)] |
||||
pub struct StatusFlags { |
||||
/// Arguments are equal
|
||||
pub equal: bool, |
||||
/// Register is zero
|
||||
pub zero: bool, |
||||
/// A < B
|
||||
pub lower: bool, |
||||
/// A > B
|
||||
pub greater: bool, |
||||
/// Register is positive
|
||||
pub positive: bool, |
||||
/// Register is negative
|
||||
pub negative: bool, |
||||
/// Overflow (multiplication etc.)
|
||||
pub overflow: bool, |
||||
/// Arithmetic carry
|
||||
pub carry: bool, |
||||
} |
||||
|
||||
impl StatusFlags { |
||||
pub fn clear(&mut self) { |
||||
*self = Self::default(); |
||||
} |
||||
|
||||
pub fn test(&self, cond: Cond) -> bool { |
||||
match cond { |
||||
Cond::Equal => self.equal, |
||||
Cond::NotEqual => !self.equal, |
||||
Cond::Zero => self.zero, |
||||
Cond::NotZero => !self.zero, |
||||
Cond::Lower => self.lower, |
||||
Cond::LowerOrEqual => self.lower || self.equal, |
||||
Cond::Greater => self.greater, |
||||
Cond::GreaterOrEqual => self.greater || self.equal, |
||||
Cond::Positive => self.positive, |
||||
Cond::NonPositive => !self.positive, |
||||
Cond::Negative => self.negative, |
||||
Cond::NonNegative => !self.negative, |
||||
Cond::Overflow => self.overflow, |
||||
Cond::NotOverflow => !self.overflow, |
||||
Cond::Carry => self.carry, |
||||
Cond::NotCarry => !self.carry |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Default, Clone, Debug)] |
||||
pub struct StackFrame { |
||||
/// Program counter, address of the executed instruction
|
||||
pub pc: Addr, |
||||
/// Status flags
|
||||
pub status: StatusFlags, |
||||
/// Argument registers
|
||||
pub arg: [Value; REG_COUNT], |
||||
/// Result registers
|
||||
pub res: [Value; REG_COUNT], |
||||
/// General purpose registers
|
||||
pub gen: [Value; REG_COUNT], |
||||
} |
||||
|
||||
impl StackFrame { |
||||
/// Create a new stack frame at a given address
|
||||
pub fn new(addr: Addr, args: &[Value]) -> Self { |
||||
let mut sf = StackFrame::default(); |
||||
sf.pc = addr; |
||||
for n in 0..(args.len().min(REG_COUNT)) { |
||||
sf.arg[n] = args[n]; |
||||
} |
||||
sf |
||||
} |
||||
|
||||
pub fn read(&mut self, rd: Rd) -> Result<u64, Fault> { |
||||
match rd.d() { |
||||
SrcDisp::Immediate(v) => Ok(v), |
||||
SrcDisp::ImmediatePtr(_) => { |
||||
unimplemented!("Immediate ptr") |
||||
} |
||||
SrcDisp::Register(Register::Res(rn)) => { |
||||
if rn >= REG_COUNT as u8 { |
||||
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
|
||||
} else { |
||||
debug!("Rd {:?} = {}", rd, self.res[rn as usize]); |
||||
Ok(self.res[rn as usize]) |
||||
} |
||||
} |
||||
SrcDisp::Register(Register::Arg(rn)) => { |
||||
if rn >= REG_COUNT as u8 { |
||||
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) }) |
||||
} else { |
||||
debug!("Rd {:?} = {}", rd, self.arg[rn as usize]); |
||||
Ok(self.arg[rn as usize]) |
||||
} |
||||
} |
||||
SrcDisp::Register(Register::Gen(rn)) => { |
||||
if rn >= REG_COUNT as u8 { |
||||
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) |
||||
} else { |
||||
debug!("Rd {:?} = {}", rd, self.gen[rn as usize]); |
||||
Ok(self.gen[rn as usize]) |
||||
} |
||||
} |
||||
SrcDisp::RegisterPtr(_) => { |
||||
unimplemented!("Register ptr") |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub fn write(&mut self, wr: Wr, val: Value) -> Result<(), Fault> { |
||||
debug!("WR {:?} := {}", wr, val); |
||||
|
||||
match wr.d() { |
||||
DstDisp::ImmediatePtr(_) => { |
||||
unimplemented!("Immediate ptr") |
||||
} |
||||
DstDisp::Register(Register::Res(rn)) => { |
||||
if rn >= REG_COUNT as u8 { |
||||
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
|
||||
} else { |
||||
Err(Fault::RegisterNotWritable { reg: Register::Res(rn) }) |
||||
} |
||||
} |
||||
DstDisp::Register(Register::Arg(rn)) => { |
||||
if rn >= REG_COUNT as u8 { |
||||
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) }) |
||||
} else { |
||||
Err(Fault::RegisterNotWritable { reg: Register::Res(rn) }) |
||||
} |
||||
} |
||||
DstDisp::Register(Register::Gen(rn)) => { |
||||
if rn >= REG_COUNT as u8 { |
||||
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) |
||||
} else { |
||||
self.gen[rn as usize] = val; |
||||
Ok(()) |
||||
} |
||||
} |
||||
DstDisp::RegisterPtr(_) => { |
||||
unimplemented!("Register ptr") |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
#[macro_use] extern crate log; |
||||
|
||||
pub mod run_thread; |
||||
pub mod mlock; |
||||
pub mod sparse; |
||||
pub mod fault; |
||||
pub mod span; |
||||
pub mod exec; |
||||
pub mod frame; |
||||
pub mod program; |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
#[test] |
||||
fn it_works() { |
||||
assert_eq!(2 + 2, 4); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,91 @@ |
||||
use std::sync::atomic::{AtomicU32, Ordering}; |
||||
use std::fmt; |
||||
use std::fmt::Formatter; |
||||
use crate::run_thread::ThreadToken; |
||||
use crate::fault::Fault; |
||||
use crate::span::MemorySpan; |
||||
|
||||
/// Records memory claims and protects from illegal access
|
||||
#[derive(Debug, Default)] |
||||
struct MemoryGuard { |
||||
claims: Vec<Claim>, |
||||
counter: AtomicU32, |
||||
} |
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)] |
||||
pub struct ClaimId(pub u32); |
||||
|
||||
impl fmt::Display for ClaimId { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
write!(f, "{}", self.0) |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, Clone)] |
||||
struct Claim { |
||||
owner: ThreadToken, |
||||
span: MemorySpan, |
||||
id: ClaimId, |
||||
} |
||||
|
||||
impl MemoryGuard { |
||||
pub fn new() -> Self { |
||||
Default::default() |
||||
} |
||||
|
||||
/// Claim a memory area
|
||||
pub fn claim(&mut self, owner: ThreadToken, span: MemorySpan) -> Result<ClaimId, Fault> { |
||||
// naive
|
||||
for claim in &self.claims { |
||||
if claim.span.intersects(span) && claim.owner != owner { |
||||
return Err(Fault::MemoryLocked { |
||||
area: claim.span, |
||||
owner: claim.owner, |
||||
}); |
||||
} |
||||
} |
||||
|
||||