From 5f4fd0e806b28e4112b4449998160bd450ade8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 24 Sep 2020 00:44:07 +0200 Subject: [PATCH] exec --- Cargo.lock | 170 +++++++++- Cargo.toml | 5 +- {csn_asm => asm}/Cargo.toml | 2 +- {csn_asm => asm}/src/data/literal.rs | 57 ++-- {csn_asm => asm}/src/data/mask.rs | 4 + asm/src/data/mod.rs | 188 +++++++++++ {csn_asm => asm}/src/data/reg.rs | 0 {csn_asm => asm}/src/error.rs | 0 {csn_asm => asm}/src/instr/cond.rs | 16 +- {csn_asm => asm}/src/instr/flatten.rs | 4 +- {csn_asm => asm}/src/instr/mod.rs | 0 {csn_asm => asm}/src/instr/op.rs | 5 +- {csn_asm => asm}/src/lib.rs | 18 +- {csn_asm => asm}/src/parse/mod.rs | 0 {csn_asm => asm}/src/parse/parse_cond.rs | 4 +- {csn_asm => asm}/src/parse/parse_data.rs | 4 +- {csn_asm => asm}/src/parse/parse_instr.rs | 0 {csn_asm => asm}/src/parse/parse_op.rs | 11 +- {csn_asm => asm}/src/parse/parse_routines.rs | 0 {csn_asm => asm}/src/parse/sexp_expect.rs | 0 {csn_asm => asm}/src/patches/mod.rs | 0 {csn_asm => asm}/src/patches/sexp_is_a.rs | 0 {csn_asm => asm}/src/patches/try_remove.rs | 0 crsn/Cargo.toml | 15 + crsn/src/main.rs | 36 +++ csn_asm/src/data/mod.rs | 97 ------ runtime/Cargo.toml | 13 + runtime/src/exec/mod.rs | 98 ++++++ runtime/src/fault.rs | 59 ++++ runtime/src/frame.rs | 150 +++++++++ runtime/src/lib.rs | 19 ++ runtime/src/mlock.rs | 91 ++++++ runtime/src/program.rs | 24 ++ runtime/src/run_thread.rs | 65 ++++ runtime/src/span.rs | 47 +++ runtime/src/sparse.rs | 317 +++++++++++++++++++ 36 files changed, 1363 insertions(+), 156 deletions(-) rename {csn_asm => asm}/Cargo.toml (91%) rename {csn_asm => asm}/src/data/literal.rs (69%) rename {csn_asm => asm}/src/data/mask.rs (94%) create mode 100644 asm/src/data/mod.rs rename {csn_asm => asm}/src/data/reg.rs (100%) rename {csn_asm => asm}/src/error.rs (100%) rename {csn_asm => asm}/src/instr/cond.rs (84%) rename {csn_asm => asm}/src/instr/flatten.rs (98%) rename {csn_asm => asm}/src/instr/mod.rs (100%) rename {csn_asm => asm}/src/instr/op.rs (90%) rename {csn_asm => asm}/src/lib.rs (95%) rename {csn_asm => asm}/src/parse/mod.rs (100%) rename {csn_asm => asm}/src/parse/parse_cond.rs (94%) rename {csn_asm => asm}/src/parse/parse_data.rs (93%) rename {csn_asm => asm}/src/parse/parse_instr.rs (100%) rename {csn_asm => asm}/src/parse/parse_op.rs (94%) rename {csn_asm => asm}/src/parse/parse_routines.rs (100%) rename {csn_asm => asm}/src/parse/sexp_expect.rs (100%) rename {csn_asm => asm}/src/patches/mod.rs (100%) rename {csn_asm => asm}/src/patches/sexp_is_a.rs (100%) rename {csn_asm => asm}/src/patches/try_remove.rs (100%) create mode 100644 crsn/Cargo.toml create mode 100644 crsn/src/main.rs delete mode 100644 csn_asm/src/data/mod.rs create mode 100644 runtime/Cargo.toml create mode 100644 runtime/src/exec/mod.rs create mode 100644 runtime/src/fault.rs create mode 100644 runtime/src/frame.rs create mode 100644 runtime/src/lib.rs create mode 100644 runtime/src/mlock.rs create mode 100644 runtime/src/program.rs create mode 100644 runtime/src/run_thread.rs create mode 100644 runtime/src/span.rs create mode 100644 runtime/src/sparse.rs diff --git a/Cargo.lock b/Cargo.lock index e842fa6..bb2af1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] -name = "csn_asm" +name = "asm" version = "0.1.0" dependencies = [ "anyhow", @@ -15,6 +15,112 @@ dependencies = [ "thiserror", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "crsn" +version = "0.1.0" +dependencies = [ + "anyhow", + "asm", + "log", + "runtime", + "simple_logger", + "thiserror", +] + +[[package]] +name = "hermit-abi" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + [[package]] name = "proc-macro2" version = "1.0.21" @@ -33,12 +139,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "asm", + "log", + "thiserror", +] + [[package]] name = "sexp" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8fa7ac9df84000b0238cf497cb2d3056bac2ff2a7d8cf179d2803b4b58571f" +[[package]] +name = "simple_logger" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a53ed2efd04911c8280f2da7bf9abd350c931b86bc7f9f2386fbafbf525ff9" +dependencies = [ + "atty", + "chrono", + "colored", + "log", + "winapi", +] + [[package]] name = "syn" version = "1.0.41" @@ -70,8 +199,47 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 38e8248..3a297ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ - "csn_asm", + "asm", + "runtime", + "crsn" ] - diff --git a/csn_asm/Cargo.toml b/asm/Cargo.toml similarity index 91% rename from csn_asm/Cargo.toml rename to asm/Cargo.toml index c5abb5f..1d75c30 100644 --- a/csn_asm/Cargo.toml +++ b/asm/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "csn_asm" +name = "asm" version = "0.1.0" authors = ["Ondřej Hruška "] edition = "2018" diff --git a/csn_asm/src/data/literal.rs b/asm/src/data/literal.rs similarity index 69% rename from csn_asm/src/data/literal.rs rename to asm/src/data/literal.rs index c76e2be..35b7bb8 100644 --- a/csn_asm/src/data/literal.rs +++ b/asm/src/data/literal.rs @@ -6,46 +6,41 @@ use std::borrow::Cow; pub type DebugMsg = Cow<'static, str>; /// Immediate value -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub struct Value(pub i64); +pub type Value = u64; -impl From for Value { - fn from(n: i64) -> Self { - Self(n) - } +pub fn is_positive(val : Value) -> bool { + 0 == (val & 0x8000_0000_0000_0000) } -impl Display for Value { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "{:#010x}", self.0) - } else { - write!(f, "{}", self.0) - } - } +pub fn is_negative(val : Value) -> bool { + 0 != (val & 0x8000_0000_0000_0000) } -impl Value { - pub fn as_u64(self) -> u64 { - u64::from_ne_bytes(self.0.to_ne_bytes()) - } - - pub fn as_u32(self) -> Option { - u32::try_from(self.as_u64()).ok() - } - - pub fn as_i32(self) -> Option { - i32::try_from(self.0).ok() - } +pub fn as_signed(val : Value) -> i64 { + i64::from_ne_bytes(val.to_ne_bytes()) } /// Immediate address -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] pub struct Addr(pub u64); +impl Addr { + pub fn advance(&mut self, add: i64) { + if add < 0 { + self.0 = self.0.wrapping_sub(-add as u64); + } else { + self.0 = self.0.wrapping_add(add as u64); + } + } +} + impl Display for Addr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "@{:#010x}", self.0) + if self.0 > 0x7fff_ffff_ffff_ffff { + write!(f, "{}", i64::from_ne_bytes(self.0.to_ne_bytes())) + } else { + write!(f, "{:#010x}", self.0) + } } } @@ -55,6 +50,12 @@ impl From for Addr { } } +impl From for u64 { + fn from(addr: Addr) -> Self { + addr.0 + } +} + /// Label name #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum Label { diff --git a/csn_asm/src/data/mask.rs b/asm/src/data/mask.rs similarity index 94% rename from csn_asm/src/data/mask.rs rename to asm/src/data/mask.rs index 1172b77..6dc4462 100644 --- a/csn_asm/src/data/mask.rs +++ b/asm/src/data/mask.rs @@ -58,4 +58,8 @@ impl Mask { pub fn as_bitmask(self) -> u64 { ((1 << self.len) - 1) << self.offset } + + pub fn is_default(self) -> bool { + self == Self::default() + } } diff --git a/asm/src/data/mod.rs b/asm/src/data/mod.rs new file mode 100644 index 0000000..ace7e70 --- /dev/null +++ b/asm/src/data/mod.rs @@ -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 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 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 for SrcDisp { + type Error = AsmError; + + fn try_from(value: DataDisp) -> Result { + 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 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 for DstDisp { + type Error = AsmError; + + fn try_from(value: DataDisp) -> Result { + 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, ")") + } +} diff --git a/csn_asm/src/data/reg.rs b/asm/src/data/reg.rs similarity index 100% rename from csn_asm/src/data/reg.rs rename to asm/src/data/reg.rs diff --git a/csn_asm/src/error.rs b/asm/src/error.rs similarity index 100% rename from csn_asm/src/error.rs rename to asm/src/error.rs diff --git a/csn_asm/src/instr/cond.rs b/asm/src/instr/cond.rs similarity index 84% rename from csn_asm/src/instr/cond.rs rename to asm/src/instr/cond.rs index bedc5ea..46b06aa 100644 --- a/csn_asm/src/instr/cond.rs +++ b/asm/src/instr/cond.rs @@ -7,8 +7,8 @@ pub enum Cond { NotEqual, Zero, NotZero, - Less, - LessOrEqual, + Lower, + LowerOrEqual, Greater, GreaterOrEqual, Positive, @@ -28,8 +28,8 @@ impl Display for Cond { Cond::NotEqual => "ne", Cond::Zero => "z", Cond::NotZero => "nz", - Cond::Less => "lt", - Cond::LessOrEqual => "le", + Cond::Lower => "lt", + Cond::LowerOrEqual => "le", Cond::Greater => "gt", Cond::GreaterOrEqual => "ge", Cond::Positive => "pos", @@ -64,10 +64,10 @@ impl Not for Cond { Cond::NotOverflow => Cond::Overflow, Cond::NotCarry => Cond::Carry, - Cond::Less => Cond::GreaterOrEqual, - Cond::Greater => Cond::LessOrEqual, - Cond::LessOrEqual => Cond::Greater, - Cond::GreaterOrEqual => Cond::Less, + Cond::Lower => Cond::GreaterOrEqual, + Cond::Greater => Cond::LowerOrEqual, + Cond::LowerOrEqual => Cond::Greater, + Cond::GreaterOrEqual => Cond::Lower, } } } diff --git a/csn_asm/src/instr/flatten.rs b/asm/src/instr/flatten.rs similarity index 98% rename from csn_asm/src/instr/flatten.rs rename to asm/src/instr/flatten.rs index b8d0eeb..e2e4e11 100644 --- a/csn_asm/src/instr/flatten.rs +++ b/asm/src/instr/flatten.rs @@ -80,7 +80,7 @@ pub fn lower(ops: Vec) -> Result, Error> { HLOp::Jump(target) => { if let Some(dest) = label_positions.get(&target) { let skip = *dest as isize - n as isize + skipped; - cleaned.push(Op::Skip(Rd::new(SrcDisp::Immediate(Value(skip as i64))))); + cleaned.push(Op::Skip(Rd::new(SrcDisp::Immediate(skip as Value)))); } else { return Err(Error::Asm(AsmError::LabelNotDefined(target))); } @@ -88,7 +88,7 @@ pub fn lower(ops: Vec) -> Result, Error> { HLOp::JumpIf(cond, target) => { if let Some(dest) = label_positions.get(&target) { let skip = *dest as isize - n as isize + skipped; - cleaned.push(Op::SkipIf(cond, Rd::new(SrcDisp::Immediate(Value(skip as i64))))); + cleaned.push(Op::SkipIf(cond, Rd::new(SrcDisp::Immediate(skip as Value)))); } else { return Err(Error::Asm(AsmError::LabelNotDefined(target))); } diff --git a/csn_asm/src/instr/mod.rs b/asm/src/instr/mod.rs similarity index 100% rename from csn_asm/src/instr/mod.rs rename to asm/src/instr/mod.rs diff --git a/csn_asm/src/instr/op.rs b/asm/src/instr/op.rs similarity index 90% rename from csn_asm/src/instr/op.rs rename to asm/src/instr/op.rs index b7a5e28..fe0705b 100644 --- a/csn_asm/src/instr/op.rs +++ b/asm/src/instr/op.rs @@ -22,6 +22,8 @@ pub enum HLOp { /// A low level instruction #[derive(Clone, Debug, Eq, PartialEq)] pub enum Op { + /// Do nothing + Nop, /// Mark a far jump target (can be jumped to from another routine). /// This label is preserved in optimized code. FarLabel(Label), @@ -48,7 +50,8 @@ pub enum Op { /// Copy a value Mov(Wr, Rd), - /// Compare two values and set conditional flags + /// Compare two values and set conditional flags. + /// If the values are identical, evaluate if they are zero, positive, or negative (the "tst" op) Cmp(Rd, Rd), // Increment a value Inc(Wr), diff --git a/csn_asm/src/lib.rs b/asm/src/lib.rs similarity index 95% rename from csn_asm/src/lib.rs rename to asm/src/lib.rs index 19df1ba..a07830f 100644 --- a/csn_asm/src/lib.rs +++ b/asm/src/lib.rs @@ -17,7 +17,7 @@ mod tests { use crate::parse::{parse, parse_instructions}; use crate::instr::{HLOp, Op, Flatten, Instr, lower}; use crate::data::{Wr, DstDisp, Register, SrcDisp, Rd}; - use crate::data::literal::{Value, Addr, Label}; + use crate::data::literal::{Addr, Label}; use std::sync::atomic::AtomicU32; use crate::instr::Cond; @@ -86,17 +86,17 @@ mod tests { // (mov r15 7) HLOp::L(Op::Mov( Wr::new(DstDisp::Register(Register::Gen(15))), - Rd::new(SrcDisp::Immediate(Value(7))), + Rd::new(SrcDisp::Immediate(7)), )), // (mov r15 0xabcd) HLOp::L(Op::Mov( Wr::new(DstDisp::Register(Register::Gen(15))), - Rd::new(SrcDisp::Immediate(Value(0xabcd))), + Rd::new(SrcDisp::Immediate(0xabcd)), )), // (mov r7 0b11110000) HLOp::L(Op::Mov( Wr::new(DstDisp::Register(Register::Gen(7))), - Rd::new(SrcDisp::Immediate(Value(0b11110000))), + Rd::new(SrcDisp::Immediate(0b11110000)), )), // (mov r7 arg1) HLOp::L(Op::Mov( @@ -250,7 +250,7 @@ mod tests { )), HLOp::Jump(Label::Numbered(0)), HLOp::Label(Label::Numbered(1)), - HLOp::JumpIf(Cond::LessOrEqual, Label::Numbered(0)), + HLOp::JumpIf(Cond::LowerOrEqual, Label::Numbered(0)), HLOp::L(Op::Mov( Wr::new(DstDisp::Register(Register::Gen(0))), Rd::new(SrcDisp::Register(Register::Gen(0))), @@ -317,8 +317,8 @@ mod tests { Wr::new(DstDisp::Register(Register::Gen(0))), Rd::new(SrcDisp::Register(Register::Gen(0))), ), - Op::Skip(Rd::new(SrcDisp::Immediate(Value(-1)))), - Op::Skip(Rd::new(SrcDisp::Immediate(Value(-2)))), + Op::Skip(Rd::new(SrcDisp::Immediate(-1))), + Op::Skip(Rd::new(SrcDisp::Immediate(-2))), Op::Mov( Wr::new(DstDisp::Register(Register::Gen(0))), Rd::new(SrcDisp::Register(Register::Gen(0))), @@ -327,8 +327,8 @@ mod tests { Wr::new(DstDisp::Register(Register::Gen(0))), Rd::new(SrcDisp::Register(Register::Gen(0))), ), - Op::Skip(Rd::new(SrcDisp::Immediate(Value(-5)))), - Op::SkipIf(Cond::Equal, Rd::new(SrcDisp::Immediate(Value(-6)))), + Op::Skip(Rd::new(SrcDisp::Immediate(-5))), + Op::SkipIf(Cond::Equal, Rd::new(SrcDisp::Immediate(-6))), Op::Barrier(Some("Routine \"foo\" overrun".into())), ], cleaned); } diff --git a/csn_asm/src/parse/mod.rs b/asm/src/parse/mod.rs similarity index 100% rename from csn_asm/src/parse/mod.rs rename to asm/src/parse/mod.rs diff --git a/csn_asm/src/parse/parse_cond.rs b/asm/src/parse/parse_cond.rs similarity index 94% rename from csn_asm/src/parse/parse_cond.rs rename to asm/src/parse/parse_cond.rs index 5ff7714..b4aec21 100644 --- a/csn_asm/src/parse/parse_cond.rs +++ b/asm/src/parse/parse_cond.rs @@ -22,8 +22,8 @@ pub fn parse_cond(text: &str) -> Result { "ne" | "<>" | "!=" | "≠" => Cond::NotEqual, "z" | "0" => Cond::Zero, "nz" | "<>0" | "!0" => Cond::NotZero, - "lt" | "<" => Cond::Less, - "le" | "<=" | "≤" => Cond::LessOrEqual, + "lt" | "<" => Cond::Lower, + "le" | "<=" | "≤" => Cond::LowerOrEqual, "gt" | ">" => Cond::Greater, "ge" | ">=" | "≥" => Cond::GreaterOrEqual, "pos" | "+" | ">0" => Cond::Positive, diff --git a/csn_asm/src/parse/parse_data.rs b/asm/src/parse/parse_data.rs similarity index 93% rename from csn_asm/src/parse/parse_data.rs rename to asm/src/parse/parse_data.rs index 96db275..88ab4a9 100644 --- a/csn_asm/src/parse/parse_data.rs +++ b/asm/src/parse/parse_data.rs @@ -23,7 +23,7 @@ pub fn parse_data_disp(tok: Option) -> Result { match &tok { Sexp::Atom(Atom::I(val)) => { - Ok(DataDisp::Immediate(Value(*val))) + Ok(DataDisp::Immediate(unsafe { std::mem::transmute(*val) })) }, Sexp::Atom(Atom::S(s)) => { if let Some(reference) = s.strip_prefix('@') { @@ -34,7 +34,7 @@ pub fn parse_data_disp(tok: Option) -> Result { Ok(DataDisp::RegisterPtr(reg::parse_reg(reference)?)) } } else if s.starts_with(|c : char| c.is_ascii_digit()) { - Ok(DataDisp::Immediate(Value(parse_i64(s)?))) + Ok(DataDisp::Immediate(unsafe { std::mem::transmute(parse_i64(s)?) })) } else { Ok(DataDisp::Register(reg::parse_reg(s)?)) } diff --git a/csn_asm/src/parse/parse_instr.rs b/asm/src/parse/parse_instr.rs similarity index 100% rename from csn_asm/src/parse/parse_instr.rs rename to asm/src/parse/parse_instr.rs diff --git a/csn_asm/src/parse/parse_op.rs b/asm/src/parse/parse_op.rs similarity index 94% rename from csn_asm/src/parse/parse_op.rs rename to asm/src/parse/parse_op.rs index 6b49034..64e9b87 100644 --- a/csn_asm/src/parse/parse_op.rs +++ b/asm/src/parse/parse_op.rs @@ -40,17 +40,17 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator { + "s" => { HLOp::L(Op::Skip(parse_rd(arg_tokens.next())?)) } - "csk" => { + "sif" => { let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?; let offs = parse_rd(arg_tokens.next())?; HLOp::L(Op::SkipIf(cond, offs)) } - "cj" => { + "jif" => { let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?; let dest = parse_label(arg_tokens.next())?; HLOp::JumpIf(cond, dest) @@ -84,6 +84,11 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator { + let arg = parse_rd(arg_tokens.next())?; + HLOp::L(Op::Cmp(arg, arg)) + } + "inc" => { HLOp::L(Op::Inc( parse_wr(arg_tokens.next())? diff --git a/csn_asm/src/parse/parse_routines.rs b/asm/src/parse/parse_routines.rs similarity index 100% rename from csn_asm/src/parse/parse_routines.rs rename to asm/src/parse/parse_routines.rs diff --git a/csn_asm/src/parse/sexp_expect.rs b/asm/src/parse/sexp_expect.rs similarity index 100% rename from csn_asm/src/parse/sexp_expect.rs rename to asm/src/parse/sexp_expect.rs diff --git a/csn_asm/src/patches/mod.rs b/asm/src/patches/mod.rs similarity index 100% rename from csn_asm/src/patches/mod.rs rename to asm/src/patches/mod.rs diff --git a/csn_asm/src/patches/sexp_is_a.rs b/asm/src/patches/sexp_is_a.rs similarity index 100% rename from csn_asm/src/patches/sexp_is_a.rs rename to asm/src/patches/sexp_is_a.rs diff --git a/csn_asm/src/patches/try_remove.rs b/asm/src/patches/try_remove.rs similarity index 100% rename from csn_asm/src/patches/try_remove.rs rename to asm/src/patches/try_remove.rs diff --git a/crsn/Cargo.toml b/crsn/Cargo.toml new file mode 100644 index 0000000..7173874 --- /dev/null +++ b/crsn/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "crsn" +version = "0.1.0" +authors = ["Ondřej Hruška "] +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" diff --git a/crsn/src/main.rs b/crsn/src/main.rs new file mode 100644 index 0000000..5d57aff --- /dev/null +++ b/crsn/src/main.rs @@ -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(); +} diff --git a/csn_asm/src/data/mod.rs b/csn_asm/src/data/mod.rs deleted file mode 100644 index 5b9b419..0000000 --- a/csn_asm/src/data/mod.rs +++ /dev/null @@ -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 for SrcDisp { - type Error = AsmError; - - fn try_from(value: DataDisp) -> Result { - 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 for DstDisp { - type Error = AsmError; - - fn try_from(value: DataDisp) -> Result { - 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()) - } -} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml new file mode 100644 index 0000000..8b39578 --- /dev/null +++ b/runtime/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "runtime" +version = "0.1.0" +authors = ["Ondřej Hruška "] +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" diff --git a/runtime/src/exec/mod.rs b/runtime/src/exec/mod.rs new file mode 100644 index 0000000..e17d7a3 --- /dev/null +++ b/runtime/src/exec/mod.rs @@ -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 { + 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, + }) + } +} diff --git a/runtime/src/fault.rs b/runtime/src/fault.rs new file mode 100644 index 0000000..46a373b --- /dev/null +++ b/runtime/src/fault.rs @@ -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, +} diff --git a/runtime/src/frame.rs b/runtime/src/frame.rs new file mode 100644 index 0000000..a2033b8 --- /dev/null +++ b/runtime/src/frame.rs @@ -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 { + 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") + } + } + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs new file mode 100644 index 0000000..88cdcb1 --- /dev/null +++ b/runtime/src/lib.rs @@ -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); + } +} + diff --git a/runtime/src/mlock.rs b/runtime/src/mlock.rs new file mode 100644 index 0000000..1503deb --- /dev/null +++ b/runtime/src/mlock.rs @@ -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, + 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 { + // naive + for claim in &self.claims { + if claim.span.intersects(span) && claim.owner != owner { + return Err(Fault::MemoryLocked { + area: claim.span, + owner: claim.owner, + }); + } + } + + let id = self.next_id(); + + self.claims.push(Claim { + id, + owner, + span, + }); + + Ok(id) + } + + /// Get a unique claim ID and increment the counter + pub fn next_id(&self) -> ClaimId { + ClaimId(self.counter.fetch_and(1, Ordering::Relaxed)) + } + + /// Get the next claim ID (ID is incremented after calling "next"). + /// May be used for release_owned_after() + pub fn epoch(&self) -> ClaimId { + ClaimId(self.counter.load(Ordering::Relaxed)) + } + + /// Release a claim by claim ID + pub fn release(&mut self, owner: ThreadToken, claim: ClaimId) -> Result<(), Fault> { + match self.claims.iter().position(|c| c.id == claim && c.owner == owner) { + Some(pos) => { + self.claims.swap_remove(pos); + Ok(()) + } + None => Err(Fault::ClaimNotExist { claim, owner }), + } + } + + /// Release all owned by a thread (thread ends) + pub fn release_owned(&mut self, owner: ThreadToken) { + self.claims.retain(|c| c.owner != owner); + } + + /// Release all owned by a thread, with claim ID >= a given value + /// (return from a subroutine) + pub fn release_owned_after(&mut self, owner: ThreadToken, epoch : ClaimId) { + self.claims.retain(|c| c.owner != owner || c.id >= epoch); + } +} diff --git a/runtime/src/program.rs b/runtime/src/program.rs new file mode 100644 index 0000000..bf8d1d7 --- /dev/null +++ b/runtime/src/program.rs @@ -0,0 +1,24 @@ +use asm::instr::Op; +use asm::data::literal::Addr; + +#[derive(Clone, Debug)] +pub struct Program { + ops: Vec, +} + +impl Program { + pub fn new(ops : Vec) -> Self { + Self { + ops, + } + } + + pub fn read(&self, addr: Addr) -> &Op { + if addr.0 >= self.ops.len() as u64 { + &Op::Nop + } else { + &self.ops[addr.0 as usize] + } + } +} + diff --git a/runtime/src/run_thread.rs b/runtime/src/run_thread.rs new file mode 100644 index 0000000..c770c73 --- /dev/null +++ b/runtime/src/run_thread.rs @@ -0,0 +1,65 @@ +use std::time::Duration; +use std::thread::JoinHandle; + +use asm::data::literal::{Addr, Value}; +use asm::instr::Op; +use crate::exec; +use asm::data::{Rd, SrcDisp, reg::Register, Wr, DstDisp}; +use crate::fault::Fault; +use crate::frame::StackFrame; +use crate::program::Program; +use crate::exec::EvalRes; + +const CYCLE_TIME : Duration = Duration::from_millis(100); + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)] +pub struct ThreadToken(pub u32); + +pub struct RunThread { + /// Thread ID + pub id: ThreadToken, + /// Active stack frame + pub frame: StackFrame, + /// Call stack + pub call_stack: Vec, + /// Program to run + pub program: Program, +} + +impl RunThread { + pub fn new(id: ThreadToken, program: Program, pc: Addr, args: &[u64]) -> Self { + let sf = StackFrame::new(pc, args); + + Self { + id, + frame: sf, + call_stack: vec![], + program, + } + } + + pub fn start(self) -> JoinHandle<()> { + std::thread::spawn(move || { + self.run(); + }) + } + + fn run(mut self) { + 'run: loop { + match self.eval_op() { + Ok(EvalRes { + cycles, advance + }) => { + std::thread::sleep(CYCLE_TIME * (cycles as u32)); + debug!("PC += {}", advance); + self.frame.pc.advance(advance); + } + Err(e) => { + error!("Fault: {:?}", e); + break 'run; + } + } + } + } +} + diff --git a/runtime/src/span.rs b/runtime/src/span.rs new file mode 100644 index 0000000..787e3c0 --- /dev/null +++ b/runtime/src/span.rs @@ -0,0 +1,47 @@ + + +use asm::data::literal::Addr; + +#[derive(Debug,Clone,Copy)] +pub struct MemorySpan { + addr: usize, + len: usize, +} + +impl MemorySpan { + pub fn new(addr: Addr, len: usize) -> MemorySpan { + if len == 0 { + panic!("Cannot create empty span!"); + } + + Self { + addr: addr.0 as usize, + len, + } + } + + /// Get start address + pub fn start(&self) -> Addr { + Addr(self.addr as u64) + } + + /// Get end address (last included byte) + pub fn last(&self) -> Addr { + Addr((self.addr + self.len - 1) as u64) + } + + /// Check if this intersects another span + pub fn intersects(&self, other: MemorySpan) -> bool { + !( + self.last() < other.start() + || self.start() > other.last() + ) + } + + /// Check if this is a strict subset of another span + pub fn inside(&self, other: MemorySpan) -> bool { + self.start() >= other.start() + && self.last() <= other.last() + } +} + diff --git a/runtime/src/sparse.rs b/runtime/src/sparse.rs new file mode 100644 index 0000000..fb00df4 --- /dev/null +++ b/runtime/src/sparse.rs @@ -0,0 +1,317 @@ +use std::io::Write; + +#[derive(Default)] +pub struct SparseBuffer { + chunks: Vec<(usize, Vec)>, +} + +impl SparseBuffer { + pub fn new() -> Self { + Default::default() + } + + pub fn write(&mut self, addr: usize, bytes: &[u8]) { + if self.chunks.is_empty() { + self.chunks.push((addr, Vec::from(bytes))); + return; + } + + enum InsertLoc { + Head, + Append(usize), + Tail, + } + + let mut loc = None; + for (i, (at, _ch)) in self.chunks.iter().enumerate() { + if *at > addr { + if i == 0 { + loc = Some(InsertLoc::Head); + } else { + loc = Some(InsertLoc::Append(i - 1)); + } + break; + } + } + + let loc = if let Some(l) = loc { + l + } else { + InsertLoc::Tail + }; + + match loc { + InsertLoc::Head => self.write_head(addr, bytes), + InsertLoc::Append(i) => self.write_after(i, addr, bytes), + InsertLoc::Tail => self.write_tail(addr, bytes), + }; + } + + fn write_head(&mut self, addr: usize, bytes: &[u8]) { + self.chunks.insert(0, (addr, vec![])); + self.write_after(0, addr, bytes); + } + + fn write_after(&mut self, index: usize, addr: usize, bytes: &[u8]) { + if index == self.chunks.len() - 1 { + self.write_tail(addr, bytes); + return; + } + + let end_addr = addr + bytes.len(); + + // This means we have at least two chunks. + // The written area can: + // - fit within the chunk + // - extend the chunk, but still end before the second's start address + // - extend the chunk, overflowing into one or more following chunks + + let (a, slice) = self.chunks.iter().nth(index + 1).unwrap(); + let second_start = *a; + let _second_len = slice.len(); + + let (a, slice) = self.chunks.get_mut(index).unwrap(); + let first_addr = *a; + let first_len = slice.len(); + + if end_addr <= first_addr + first_len { + (&mut slice[(addr - first_addr) as usize..]).write(bytes); + } else if end_addr <= second_start { + slice.truncate((addr - first_addr) as usize); + slice.extend_from_slice(bytes); + } else { + // overflows into one or more chunks + slice.truncate((addr - first_addr) as usize); + slice.extend_from_slice(&bytes[..(second_start - addr) as usize]); + + // recurse + self.write_after(index + 1, second_start, &bytes[(second_start - addr) as usize..]); + } + } + + fn write_tail(&mut self, addr: usize, bytes: &[u8]) { + let (a, slice) = self.chunks.last_mut().unwrap(); + + let last_addr = *a; + let last_len = slice.len(); + let end_addr = addr + bytes.len(); + + assert!(addr >= last_addr); + + if end_addr <= last_addr + last_len { + // Entirely contained within the last chunk + (&mut slice[((addr - last_addr) as usize)..]).write(bytes); + } else if addr > last_addr + last_len { + self.chunks.push((addr, Vec::from(bytes))); + } else { + // The write slice starts within the last chunk, but extends past its end. + slice.truncate((addr - last_addr) as usize); + slice.extend_from_slice(bytes); + } + } + + pub fn coalesce(&mut self) { + let mut prev = Option::<(usize, Vec)>::None; + let mut merged = vec![]; + for chunk in self.chunks.drain(..) { + if let Some(prevchunk) = &mut prev { + if chunk.0 == prevchunk.0 + prevchunk.1.len() { + prevchunk.1.extend_from_slice(&chunk.1[..]); + continue; + } else { + merged.push(prev.take().unwrap()); + } + } + prev = Some(chunk); + } + if let Some(prevchunk) = prev { + merged.push(prevchunk); + } + self.chunks = merged; + } +} + +#[cfg(test)] +mod tests { + use super::SparseBuffer; + + #[test] + fn test_empty() { + let mut sparse = SparseBuffer::new(); + + sparse.write(100, &[0, 1, 2, 3, 4]); + assert_eq!(vec![(100, vec![0, 1, 2, 3, 4])], sparse.chunks); + } + + #[test] + fn test_append_sparse() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + sparse.write(100, &[7, 8, 9, 10]); + assert_eq!(vec![ + (0, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + (100, vec![7, 8, 9, 10]) + ], sparse.chunks); + } + + #[test] + fn test_append_inside_last() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + sparse.write(7, &[70, 80, 90]); + assert_eq!(vec![ + (0, vec![0, 1, 2, 3, 4, 5, 6, 70, 80, 90, 10]), + ], sparse.chunks); + } + + #[test] + fn test_append_extend_last() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + sparse.write(5, &[50, 60, 70, 80, 90, 100, 110, 120, 130, 140]); + assert_eq!(vec![(0, vec![0, 1, 2, 3, 4, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140])], sparse.chunks); + } + + #[test] + fn test_prepend_sparse() { + let mut sparse = SparseBuffer::new(); + + sparse.write(100, &[0, 1, 2, 3, 4]); + sparse.write(10, &[70, 80, 90, 100]); + + assert_eq!(vec![ + (10, vec![70, 80, 90, 100]), + (100, vec![0, 1, 2, 3, 4]) + ], sparse.chunks); + } + + #[test] + fn test_within_first() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + sparse.write(4, &[40, 50, 60]); + + assert_eq!(vec![ + (0, vec![0, 1, 2, 3, 40, 50, 60, 7, 8, 9]), + ], sparse.chunks); + } + + #[test] + fn test_grows_first() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2, 3, 4, 5]); + sparse.write(10, &[10, 11, 12]); + sparse.write(4, &[40, 50, 60]); + + assert_eq!(vec![ + (0, vec![0, 1, 2, 3, 40, 50, 60]), + (10, vec![10, 11, 12]), + ], sparse.chunks); + } + + #[test] + fn test_grows_first2() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2, 3, 4, 5]); + sparse.write(10, &[10, 11, 12]); + sparse.write(4, &[40, 50, 60, 70, 80, 90]); + + assert_eq!(vec![ + (0, vec![0, 1, 2, 3, 40, 50, 60, 70, 80, 90]), + (10, vec![10, 11, 12]), + ], sparse.chunks); + } + + #[test] + fn test_overflow_first() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2, 3, 4, 5]); + sparse.write(10, &[10, 11, 12, 13, 14, 15]); + sparse.write(4, &[40, 50, 60, 70, 80, 90, 100, 110, 120]); + + assert_eq!(vec![ + (0, vec![0, 1, 2, 3, 40, 50, 60, 70, 80, 90]), + (10, vec![100, 110, 120, 13, 14, 15]), + ], sparse.chunks); + } + + #[test] + fn test_join_tail() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2]); + sparse.write(3, &[3, 4, 5]); + sparse.write(6, &[6, 7, 8]); + + assert_eq!(vec![ + (0, vec![0, 1, 2, 3, 4, 5, 6, 7, 8]), + ], sparse.chunks); + } + + #[test] + fn test_overflow_multiple() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2]); + sparse.write(4, &[4, 5, 6]); + sparse.write(8, &[8, 9, 10]); + + assert_eq!(vec![ + (0, vec![0, 1, 2]), + (4, vec![4, 5, 6]), + (8, vec![8, 9, 10]), + ], sparse.chunks); + + sparse.write(2, &[20, 30, 40, 50, 60, 70, 80, 90]); + + assert_eq!(vec![ + (0, vec![0, 1, 20, 30]), + (4, vec![40, 50, 60, 70]), + (8, vec![80, 90, 10]), + ], sparse.chunks); + } + + #[test] + fn test_overflow_multiple2() { + let mut sparse = SparseBuffer::new(); + + sparse.write(0, &[0, 1, 2]); + sparse.write(4, &[4, 5, 6]); + sparse.write(8, &[8, 9, 10]); + + assert_eq!(vec![ + (0, vec![0, 1, 2]), + (4, vec![4, 5, 6]), + (8, vec![8, 9, 10]), + ], sparse.chunks); + sparse.coalesce(); + + // no change, as expected + assert_eq!(vec![ + (0, vec![0, 1, 2]), + (4, vec![4, 5, 6]), + (8, vec![8, 9, 10]), + ], sparse.chunks); + + sparse.write(2, &[20, 30, 40, 50, 60, 70, 80, 90, 100, 110]); + + assert_eq!(vec![ + (0, vec![0, 1, 20, 30]), + (4, vec![40, 50, 60, 70]), + (8, vec![80, 90, 100, 110]), + ], sparse.chunks); + + // join contiguous + sparse.coalesce(); + assert_eq!(vec![ + (0, vec![0, 1, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110]), + ], sparse.chunks); + } +}