diff --git a/Cargo.lock b/Cargo.lock index 608fdd7..befd25f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "aluvm" -version = "0.11.0-beta.1" +version = "0.11.0-beta.2" dependencies = [ "amplify", "baid58", @@ -66,11 +66,12 @@ dependencies = [ [[package]] name = "amplify_num" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddce3bc63e807ea02065e8d8b702695f3d302ae4158baddff8b0ce5c73947251" +checksum = "9681187211554ab98f138ba159e90861b136c20afc680dcff2ba82d020721e27" dependencies = [ "serde", + "wasm-bindgen", ] [[package]] @@ -308,9 +309,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -330,15 +331,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -351,9 +352,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "log" @@ -379,9 +380,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "paste" @@ -397,9 +398,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -463,9 +464,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "scoped-tls" @@ -484,31 +485,31 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" dependencies = [ "cc", ] [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -524,9 +525,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -543,9 +544,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.27" +version = "0.9.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129" dependencies = [ "indexmap", "itoa", @@ -633,9 +634,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -690,9 +691,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "version_check" @@ -714,9 +715,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -724,24 +725,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -751,9 +752,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -761,28 +762,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-bindgen-test" -version = "0.3.38" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6433b7c56db97397842c46b67e11873eda263170afeb3a2dc74a7cb370fee0d" +checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403" dependencies = [ "console_error_panic_hook", "js-sys", @@ -794,20 +795,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.38" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735" +checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -815,9 +816,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 8ff4836..bfc7daf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "aluvm" description = "Functional registry-based RISC virtual machine" -version = "0.11.0-beta.1" +version = "0.11.0-beta.2" authors = ["Dr Maxim Orlovsky "] repository = "https://github.com/aluvm/rust-aluvm" homepage = "https://aluvm.org" diff --git a/src/data/byte_str.rs b/src/data/byte_str.rs index cf961d0..e3ea646 100644 --- a/src/data/byte_str.rs +++ b/src/data/byte_str.rs @@ -216,7 +216,32 @@ impl Display for ByteStr { // 4)..].to_hex()) } else if let Ok(s) = String::from_utf8(vec) { f.write_str("\"")?; - f.write_str(&s)?; + let mut ctl = false; + for c in s.chars() { + let v = c as u32; + if c.is_control() || v <= 0x21 || v >= 0x7F { + if !ctl { + f.write_str("\x1B[2;3m<\x1B[1m")?; + ctl = true; + } + if v <= 0xFF { + write!(f, "{v:02X}")?; + } else if v <= 0xFFFF { + write!(f, "{v:04X}")?; + } else { + write!(f, "{v:08X}")?; + } + } else { + if ctl { + f.write_str("\x1B[2m>\x1B[1;23m")?; + ctl = false; + } + f.write_char(c)?; + } + } + if ctl { + f.write_str("\x1B[2m>\x1B[1;23m")?; + } f.write_str("\"") } else { f.write_str(&self.as_ref().to_hex()) diff --git a/src/data/mod.rs b/src/data/mod.rs index 7112afe..59868da 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -34,3 +34,34 @@ pub use byte_str::ByteStr; pub use number::{ FloatLayout, IntLayout, Layout, LiteralParseError, MaybeNumber, Number, NumberLayout, Step, }; + +/// Value which can be extracted from any register. +#[allow(clippy::large_enum_variant)] +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +pub enum RegValue { + /// Value extracted from numerical registers + #[from] + #[from(Number)] + Number(MaybeNumber), + + /// Value extracted from string register + #[from] + #[from(ByteStr)] + String(Option), +} + +mod display { + use core::fmt::{self, Display, Formatter}; + + use super::*; + + impl Display for RegValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + RegValue::Number(n) => Display::fmt(n, f), + RegValue::String(Some(s)) => Display::fmt(s, f), + RegValue::String(None) => f.write_str("~"), + } + } + } +} diff --git a/src/data/number.rs b/src/data/number.rs index 5a41b68..957b680 100644 --- a/src/data/number.rs +++ b/src/data/number.rs @@ -75,37 +75,71 @@ pub enum Layout { } impl Layout { + /// Unsigned 8-bit layout + pub const U8: Self = Self::unsigned(1); + /// Unsigned 16-bit layout + pub const U16: Self = Self::unsigned(2); + /// Unsigned 24-bit layout + pub const U24: Self = Self::unsigned(3); + /// Unsigned 32-bit layout + pub const U32: Self = Self::unsigned(4); + /// Unsigned 48-bit layout + pub const U48: Self = Self::unsigned(6); + /// Unsigned 64-bit layout + pub const U64: Self = Self::unsigned(8); + /// Unsigned 128-bit layout + pub const U128: Self = Self::unsigned(16); + /// Unsigned 256-bit layout + pub const U256: Self = Self::unsigned(32); + + /// Signed 8-bit layout + pub const I8: Self = Self::signed(1); + /// Signed 16-bit layout + pub const I16: Self = Self::signed(2); + /// Signed 24-bit layout + pub const I24: Self = Self::signed(3); + /// Signed 32-bit layout + pub const I32: Self = Self::signed(4); + /// Signed 48-bit layout + pub const I48: Self = Self::signed(6); + /// Signed 64-bit layout + pub const I64: Self = Self::signed(8); + /// Signed 128-bit layout + pub const I128: Self = Self::signed(16); + /// Signed 256-bit layout + pub const I256: Self = Self::signed(32); + /// Returns signed integer layout #[inline] - pub fn signed(bytes: u16) -> Layout { Layout::Integer(IntLayout::signed(bytes)) } + pub const fn signed(bytes: u16) -> Layout { Layout::Integer(IntLayout::signed(bytes)) } /// Returns unsigned integer layout #[inline] - pub fn unsigned(bytes: u16) -> Layout { Layout::Integer(IntLayout::unsigned(bytes)) } + pub const fn unsigned(bytes: u16) -> Layout { Layout::Integer(IntLayout::unsigned(bytes)) } /// Constructs float layout #[inline] - pub fn float(layout: FloatLayout) -> Layout { Layout::Float(layout) } + pub const fn float(layout: FloatLayout) -> Layout { Layout::Float(layout) } /// Detects if the number layout is unsigned integer #[inline] - pub fn is_unsigned_int(self) -> bool { + pub const fn is_unsigned_int(self) -> bool { matches!(self, Layout::Integer(IntLayout { signed: false, .. })) } /// Detects if the number layout is signed integer #[inline] - pub fn is_signed_int(self) -> bool { + pub const fn is_signed_int(self) -> bool { matches!(self, Layout::Integer(IntLayout { signed: true, .. })) } /// Detects if the number layout is one of integer (signed or unsigned) layouts #[inline] - pub fn is_integer(self) -> bool { matches!(self, Layout::Integer(_)) } + pub const fn is_integer(self) -> bool { matches!(self, Layout::Integer(_)) } /// Detects if the number layout is one of float layouts #[inline] - pub fn is_float(self) -> bool { matches!(self, Layout::Float(_)) } + pub const fn is_float(self) -> bool { matches!(self, Layout::Float(_)) } /// Converts unsigned integer layout into signed; does nothing for float layouts #[inline] @@ -188,31 +222,65 @@ impl Display for IntLayout { } impl IntLayout { + /// Unsigned 8-bit layout + pub const U8: Self = Self::unsigned(1); + /// Unsigned 16-bit layout + pub const U16: Self = Self::unsigned(2); + /// Unsigned 24-bit layout + pub const U24: Self = Self::unsigned(3); + /// Unsigned 32-bit layout + pub const U32: Self = Self::unsigned(4); + /// Unsigned 48-bit layout + pub const U48: Self = Self::unsigned(6); + /// Unsigned 64-bit layout + pub const U64: Self = Self::unsigned(8); + /// Unsigned 128-bit layout + pub const U128: Self = Self::unsigned(16); + /// Unsigned 256-bit layout + pub const U256: Self = Self::unsigned(32); + + /// Signed 8-bit layout + pub const I8: Self = Self::signed(1); + /// Signed 16-bit layout + pub const I16: Self = Self::signed(2); + /// Signed 24-bit layout + pub const I24: Self = Self::signed(3); + /// Signed 32-bit layout + pub const I32: Self = Self::signed(4); + /// Signed 48-bit layout + pub const I48: Self = Self::signed(6); + /// Signed 64-bit layout + pub const I64: Self = Self::signed(8); + /// Signed 128-bit layout + pub const I128: Self = Self::signed(16); + /// Signed 256-bit layout + pub const I256: Self = Self::signed(32); + /// Returns signed integer layout #[inline] - pub fn signed(bytes: u16) -> IntLayout { Self { signed: true, bytes } } + pub const fn signed(bytes: u16) -> IntLayout { Self { signed: true, bytes } } /// Returns unsigned integer layout #[inline] - pub fn unsigned(bytes: u16) -> IntLayout { Self { signed: false, bytes } } + pub const fn unsigned(bytes: u16) -> IntLayout { Self { signed: false, bytes } } /// Converts unsigned integer layout into signed #[inline] - pub fn into_signed(mut self) -> IntLayout { + pub const fn into_signed(mut self) -> IntLayout { self.signed = true; self } /// Converts signed integer layout into unsigned #[inline] - pub fn into_unsigned(mut self) -> IntLayout { + pub const fn into_unsigned(mut self) -> IntLayout { self.signed = false; self } /// Updates layout (if used) to match signed/unsigned format of some other layout. #[inline] - pub fn using_sign(mut self, other: IntLayout) -> IntLayout { + pub const fn using_sign(mut self, other: IntLayout) -> IntLayout { self.signed = other.signed; self } @@ -380,13 +448,61 @@ impl From<&FloatLayout> for Layout { pub struct MaybeNumber(Option); impl MaybeNumber { + /// Zero value in unsigned 8-bit layout + pub const ZERO_U8: Self = Self::zero(Layout::U8); + /// Zero value in unsigned 16-bit layout + pub const ZERO_U16: Self = Self::zero(Layout::U16); + /// Zero value in unsigned 24-bit layout + pub const ZERO_U24: Self = Self::zero(Layout::U24); + /// Zero value in unsigned 32-bit layout + pub const ZERO_U32: Self = Self::zero(Layout::U32); + /// Zero value in unsigned 48-bit layout + pub const ZERO_U48: Self = Self::zero(Layout::U48); + /// Zero value in unsigned 64-bit layout + pub const ZERO_U64: Self = Self::zero(Layout::U64); + /// Zero value in unsigned 128-bit layout + pub const ZERO_U128: Self = Self::zero(Layout::U128); + /// Zero value in unsigned 256-bit layout + pub const ZERO_U256: Self = Self::zero(Layout::U256); + + /// Value of 1 in unsigned 8-bit layout + pub const ONE_U8: Self = Self::one(Layout::U8); + /// Value of 1 in unsigned 16-bit layout + pub const ONE_U16: Self = Self::one(Layout::U16); + /// Value of 1 in unsigned 24-bit layout + pub const ONE_U24: Self = Self::one(Layout::U24); + /// Value of 1 in unsigned 32-bit layout + pub const ONE_U32: Self = Self::one(Layout::U32); + /// Value of 1 in unsigned 48-bit layout + pub const ONE_U48: Self = Self::one(Layout::U48); + /// Value of 1 in unsigned 64-bit layout + pub const ONE_U64: Self = Self::one(Layout::U64); + /// Value of 1 in unsigned 128-bit layout + pub const ONE_U128: Self = Self::one(Layout::U128); + /// Value of 1 in unsigned 256-bit layout + pub const ONE_U256: Self = Self::one(Layout::U256); + /// Creates [`MaybeNumber`] without assigning a value to it #[inline] - pub fn none() -> MaybeNumber { MaybeNumber(None) } + pub const fn none() -> MaybeNumber { MaybeNumber(None) } /// Creates [`MaybeNumber`] assigning a value to it #[inline] - pub fn some(val: Number) -> MaybeNumber { MaybeNumber(Some(val)) } + pub const fn some(val: Number) -> MaybeNumber { MaybeNumber(Some(val)) } + + /// Creates zero value with a given layout + #[inline] + pub const fn zero(layout: Layout) -> MaybeNumber { + Self::some(Number { layout, bytes: [0u8; 1024] }) + } + + /// Creates one value with a given layout + #[inline] + pub const fn one(layout: Layout) -> MaybeNumber { + let mut n = Number { layout, bytes: [0u8; 1024] }; + n.bytes[0] = 1; + Self::some(n) + } /// Transforms internal value layout returning whether this was possible without discarding any /// bit information @@ -434,6 +550,51 @@ impl Display for MaybeNumber { } } +impl Octal for MaybeNumber { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.0 { + None => f.write_str("~"), + Some(ref val) => Octal::fmt(val, f), + } + } +} + +impl LowerHex for MaybeNumber { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.0 { + None => f.write_str("~"), + Some(ref val) => LowerHex::fmt(val, f), + } + } +} + +impl UpperHex for MaybeNumber { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.0 { + None => f.write_str("~"), + Some(ref val) => UpperHex::fmt(val, f), + } + } +} + +impl LowerExp for MaybeNumber { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.0 { + None => f.write_str("~"), + Some(ref val) => LowerExp::fmt(val, f), + } + } +} + +impl UpperExp for MaybeNumber { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.0 { + None => f.write_str("~"), + Some(ref val) => UpperExp::fmt(val, f), + } + } +} + impl FromStr for MaybeNumber { type Err = LiteralParseError; @@ -589,9 +750,51 @@ impl IndexMut> for Number { } impl Number { + /// Zero value in unsigned 8-bit layout + pub const ZERO_U8: Self = Self::zero(Layout::U8); + /// Zero value in unsigned 16-bit layout + pub const ZERO_U16: Self = Self::zero(Layout::U16); + /// Zero value in unsigned 24-bit layout + pub const ZERO_U24: Self = Self::zero(Layout::U24); + /// Zero value in unsigned 32-bit layout + pub const ZERO_U32: Self = Self::zero(Layout::U32); + /// Zero value in unsigned 48-bit layout + pub const ZERO_U48: Self = Self::zero(Layout::U48); + /// Zero value in unsigned 64-bit layout + pub const ZERO_U64: Self = Self::zero(Layout::U64); + /// Zero value in unsigned 128-bit layout + pub const ZERO_U128: Self = Self::zero(Layout::U128); + /// Zero value in unsigned 256-bit layout + pub const ZERO_U256: Self = Self::zero(Layout::U256); + + /// Value of 1 in unsigned 8-bit layout + pub const ONE_U8: Self = Self::one(Layout::U8); + /// Value of 1 in unsigned 16-bit layout + pub const ONE_U16: Self = Self::one(Layout::U16); + /// Value of 1 in unsigned 24-bit layout + pub const ONE_U24: Self = Self::one(Layout::U24); + /// Value of 1 in unsigned 32-bit layout + pub const ONE_U32: Self = Self::one(Layout::U32); + /// Value of 1 in unsigned 48-bit layout + pub const ONE_U48: Self = Self::one(Layout::U48); + /// Value of 1 in unsigned 64-bit layout + pub const ONE_U64: Self = Self::one(Layout::U64); + /// Value of 1 in unsigned 128-bit layout + pub const ONE_U128: Self = Self::one(Layout::U128); + /// Value of 1 in unsigned 256-bit layout + pub const ONE_U256: Self = Self::one(Layout::U256); + /// Creates zero value with a given layout #[inline] - pub fn zero(layout: Layout) -> Number { Number { layout, bytes: [0u8; 1024] } } + pub const fn zero(layout: Layout) -> Number { Number { layout, bytes: [0u8; 1024] } } + + /// Creates one value with a given layout + #[inline] + pub const fn one(layout: Layout) -> Number { + let mut n = Number { layout, bytes: [0u8; 1024] }; + n.bytes[0] = 1; + n + } /// Creates value with the specified bit masked #[inline] diff --git a/src/isa/bytecode.rs b/src/isa/bytecode.rs index d6def7c..525e4fc 100644 --- a/src/isa/bytecode.rs +++ b/src/isa/bytecode.rs @@ -234,14 +234,6 @@ where } impl Bytecode for ControlFlowOp { - #[inline] - fn call_site(&self) -> Option { - match self { - ControlFlowOp::Call(site) | ControlFlowOp::Exec(site) => Some(*site), - _ => None, - } - } - fn byte_count(&self) -> u16 { match self { ControlFlowOp::Fail | ControlFlowOp::Succ => 1, @@ -269,6 +261,14 @@ impl Bytecode for ControlFlowOp { } } + #[inline] + fn call_site(&self) -> Option { + match self { + ControlFlowOp::Call(site) | ControlFlowOp::Exec(site) => Some(*site), + _ => None, + } + } + fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> where W: Write, diff --git a/src/isa/exec.rs b/src/isa/exec.rs index 6555dc2..171f81d 100644 --- a/src/isa/exec.rs +++ b/src/isa/exec.rs @@ -27,6 +27,7 @@ use alloc::string::String; use alloc::vec::Vec; use core::cmp::Ordering; use core::ops::{BitAnd, BitOr, BitXor, Neg, Rem, Shl, Shr}; +use std::collections::HashSet; use sha2::Digest; @@ -37,7 +38,7 @@ use super::{ use crate::data::{ByteStr, MaybeNumber, Number, NumberLayout}; use crate::isa::{ExtendFlag, FloatEqFlag, IntFlags, MergeFlag, NoneEqFlag, SignFlag}; use crate::library::{constants, LibSite}; -use crate::reg::{CoreRegs, NumericRegister, Reg32, RegA, RegA2, RegAR, RegR}; +use crate::reg::{CoreRegs, NumericRegister, Reg, Reg32, RegA, RegA2, RegAR, RegBlockAR, RegR}; /// Turing machine movement after instruction execution #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -82,6 +83,19 @@ pub trait InstructionSet: Bytecode + core::fmt::Display + core::fmt::Debug { #[inline] fn is_supported(id: &str) -> bool { Self::isa_ids().contains(id) } + /// Lists all registers which are used by the instruction. + fn regs(&self) -> HashSet { + let mut regs = self.src_regs(); + regs.extend(self.dst_regs()); + regs + } + + /// List of registers which value is taken into the account by the instruction. + fn src_regs(&self) -> HashSet; + + /// List of registers which value may be changed by the instruction. + fn dst_regs(&self) -> HashSet; + /// Returns computational complexity of the instruction #[inline] fn complexity(&self) -> u64 { 1 } @@ -117,6 +131,46 @@ where set } + fn src_regs(&self) -> HashSet { + match self { + Instr::ControlFlow(instr) => instr.src_regs(), + Instr::Put(instr) => instr.src_regs(), + Instr::Move(instr) => instr.src_regs(), + Instr::Cmp(instr) => instr.src_regs(), + Instr::Arithmetic(instr) => instr.src_regs(), + Instr::Bitwise(instr) => instr.src_regs(), + Instr::Bytes(instr) => instr.src_regs(), + Instr::Digest(instr) => instr.src_regs(), + #[cfg(feature = "secp256k1")] + Instr::Secp256k1(instr) => instr.src_regs(), + #[cfg(feature = "curve25519")] + Instr::Curve25519(instr) => instr.src_regs(), + Instr::ExtensionCodes(instr) => instr.src_regs(), + Instr::ReservedInstruction(instr) => instr.src_regs(), + Instr::Nop => set![], + } + } + + fn dst_regs(&self) -> HashSet { + match self { + Instr::ControlFlow(instr) => instr.dst_regs(), + Instr::Put(instr) => instr.dst_regs(), + Instr::Move(instr) => instr.dst_regs(), + Instr::Cmp(instr) => instr.dst_regs(), + Instr::Arithmetic(instr) => instr.dst_regs(), + Instr::Bitwise(instr) => instr.dst_regs(), + Instr::Bytes(instr) => instr.dst_regs(), + Instr::Digest(instr) => instr.dst_regs(), + #[cfg(feature = "secp256k1")] + Instr::Secp256k1(instr) => instr.dst_regs(), + #[cfg(feature = "curve25519")] + Instr::Curve25519(instr) => instr.dst_regs(), + Instr::ExtensionCodes(instr) => instr.dst_regs(), + Instr::ReservedInstruction(instr) => instr.dst_regs(), + Instr::Nop => set![], + } + } + #[inline] fn exec(&self, regs: &mut CoreRegs, site: LibSite, ctx: &Self::Context<'_>) -> ExecStep { match self { @@ -145,6 +199,10 @@ impl InstructionSet for ControlFlowOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { set![] } + + fn dst_regs(&self) -> HashSet { set![] } + #[inline] fn complexity(&self) -> u64 { 2 } @@ -188,42 +246,55 @@ impl InstructionSet for PutOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { set![] } + + fn dst_regs(&self) -> HashSet { + match self { + PutOp::ClrA(_, _) | PutOp::ClrF(_, _) | PutOp::ClrR(_, _) => set![], + PutOp::PutA(reg, reg32, _) => set![Reg::A(*reg, *reg32)], + PutOp::PutF(reg, reg32, _) => set![Reg::F(*reg, *reg32)], + PutOp::PutR(reg, reg32, _) => set![Reg::R(*reg, *reg32)], + PutOp::PutIfA(reg, reg32, _) => set![Reg::A(*reg, *reg32)], + PutOp::PutIfR(reg, reg32, _) => set![Reg::R(*reg, *reg32)], + } + } + #[inline] fn complexity(&self) -> u64 { 2 } fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { match self { PutOp::ClrA(reg, index) => { - regs.set(reg, index, MaybeNumber::none()); + regs.set_n(reg, index, MaybeNumber::none()); } PutOp::ClrF(reg, index) => { - regs.set(reg, index, MaybeNumber::none()); + regs.set_n(reg, index, MaybeNumber::none()); } PutOp::ClrR(reg, index) => { - regs.set(reg, index, MaybeNumber::none()); + regs.set_n(reg, index, MaybeNumber::none()); } PutOp::PutA(reg, index, number) => { - if !regs.set(reg, index, **number) { + if !regs.set_n(reg, index, **number) { regs.st0 = false; } } PutOp::PutF(reg, index, number) => { - if !regs.set(reg, index, **number) { + if !regs.set_n(reg, index, **number) { regs.st0 = false; } } PutOp::PutR(reg, index, number) => { - if !regs.set(reg, index, **number) { + if !regs.set_n(reg, index, **number) { regs.st0 = false; } } PutOp::PutIfA(reg, index, number) => { - if !regs.set_if(reg, index, **number) { + if !regs.set_n_if(reg, index, **number) { regs.st0 = false; } } PutOp::PutIfR(reg, index, number) => { - if !regs.set_if(reg, index, **number) { + if !regs.set_n_if(reg, index, **number) { regs.st0 = false; } } @@ -238,76 +309,178 @@ impl InstructionSet for MoveOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { + match self { + MoveOp::MovA(reg, idx1, _idx2) => { + set![Reg::A(*reg, *idx1)] + } + MoveOp::DupA(reg, idx1, _idx2) => { + set![Reg::A(*reg, *idx1)] + } + MoveOp::SwpA(reg, idx1, idx2) => { + set![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] + } + MoveOp::MovF(reg, idx1, _idx2) => { + set![Reg::F(*reg, *idx1)] + } + MoveOp::DupF(reg, idx1, _idx2) => { + set![Reg::F(*reg, *idx1)] + } + MoveOp::SwpF(reg, idx1, idx2) => { + set![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] + } + MoveOp::MovR(reg, idx1, _idx2) => { + set![Reg::R(*reg, *idx1)] + } + MoveOp::DupR(reg, idx1, _idx2) => { + set![Reg::R(*reg, *idx1)] + } + + MoveOp::CpyA(sreg, sidx, _dreg, _didx) => { + set![Reg::A(*sreg, *sidx)] + } + MoveOp::CnvA(sreg, sidx, _dreg, _didx) => { + set![Reg::A(*sreg, *sidx)] + } + MoveOp::CnvF(sreg, sidx, _dreg, _didx) => { + set![Reg::F(*sreg, *sidx)] + } + MoveOp::CpyR(sreg, sidx, _dreg, _didx) => { + set![Reg::R(*sreg, *sidx)] + } + MoveOp::SpyAR(sreg, sidx, dreg, didx) => { + set![Reg::A(*sreg, *sidx), Reg::R(*dreg, *didx)] + } + MoveOp::CnvAF(sreg, sidx, _dreg, _didx) => { + set![Reg::A(*sreg, *sidx)] + } + MoveOp::CnvFA(sreg, sidx, _dreg, _didx) => { + set![Reg::F(*sreg, *sidx)] + } + } + } + + fn dst_regs(&self) -> HashSet { + match self { + MoveOp::MovA(reg, idx1, idx2) => { + set![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] + } + MoveOp::DupA(reg, _idx1, idx2) => { + set![Reg::A(*reg, *idx2)] + } + MoveOp::SwpA(reg, idx1, idx2) => { + set![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] + } + MoveOp::MovF(reg, idx1, idx2) => { + set![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] + } + MoveOp::DupF(reg, _idx1, idx2) => { + set![Reg::F(*reg, *idx2)] + } + MoveOp::SwpF(reg, idx1, idx2) => { + set![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] + } + MoveOp::MovR(reg, idx1, idx2) => { + set![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] + } + MoveOp::DupR(reg, _idx1, idx2) => { + set![Reg::R(*reg, *idx2)] + } + + MoveOp::CpyA(_sreg, _sidx, dreg, didx) => { + set![Reg::A(*dreg, *didx)] + } + MoveOp::CnvA(_sreg, _sidx, dreg, didx) => { + set![Reg::A(*dreg, *didx)] + } + MoveOp::CnvF(_sreg, _sidx, dreg, didx) => { + set![Reg::F(*dreg, *didx)] + } + MoveOp::CpyR(_sreg, _sidx, dreg, didx) => { + set![Reg::R(*dreg, *didx)] + } + MoveOp::SpyAR(sreg, sidx, dreg, didx) => { + set![Reg::A(*sreg, *sidx), Reg::R(*dreg, *didx)] + } + MoveOp::CnvAF(_sreg, _sidx, dreg, didx) => { + set![Reg::F(*dreg, *didx)] + } + MoveOp::CnvFA(_sreg, _sidx, dreg, didx) => { + set![Reg::A(*dreg, *didx)] + } + } + } + fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { match self { MoveOp::MovA(reg, idx1, idx2) => { - regs.set(reg, idx2, regs.get(reg, idx1)); - regs.set(reg, idx1, MaybeNumber::none()); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); + regs.set_n(reg, idx1, MaybeNumber::none()); } MoveOp::DupA(reg, idx1, idx2) => { - regs.set(reg, idx2, regs.get(reg, idx1)); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); } MoveOp::SwpA(reg, idx1, idx2) => { - let val = regs.get(reg, idx2); - regs.set(reg, idx2, regs.get(reg, idx1)); - regs.set(reg, idx1, val); + let val = regs.get_n(reg, idx2); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); + regs.set_n(reg, idx1, val); } MoveOp::MovF(reg, idx1, idx2) => { - regs.set(reg, idx2, regs.get(reg, idx1)); - regs.set(reg, idx1, MaybeNumber::none()); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); + regs.set_n(reg, idx1, MaybeNumber::none()); } MoveOp::DupF(reg, idx1, idx2) => { - regs.set(reg, idx2, regs.get(reg, idx1)); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); } MoveOp::SwpF(reg, idx1, idx2) => { - let val = regs.get(reg, idx2); - regs.set(reg, idx2, regs.get(reg, idx1)); - regs.set(reg, idx1, val); + let val = regs.get_n(reg, idx2); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); + regs.set_n(reg, idx1, val); } MoveOp::MovR(reg, idx1, idx2) => { - regs.set(reg, idx2, regs.get(reg, idx1)); - regs.set(reg, idx1, MaybeNumber::none()); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); + regs.set_n(reg, idx1, MaybeNumber::none()); } MoveOp::DupR(reg, idx1, idx2) => { - regs.set(reg, idx2, regs.get(reg, idx1)); + regs.set_n(reg, idx2, regs.get_n(reg, idx1)); } MoveOp::CpyA(sreg, sidx, dreg, didx) => { - let mut val = regs.get(sreg, sidx); + let mut val = regs.get_n(sreg, sidx); regs.st0 = val.reshape(dreg.layout()); - regs.set(dreg, didx, val); + regs.set_n(dreg, didx, val); } MoveOp::CnvA(sreg, sidx, dreg, didx) => { - let mut val = regs.get(sreg, sidx); + let mut val = regs.get_n(sreg, sidx); regs.st0 = val.reshape(dreg.layout().into_signed()); - regs.set(dreg, didx, val); + regs.set_n(dreg, didx, val); } MoveOp::CnvF(sreg, sidx, dreg, didx) => { - let mut val = regs.get(sreg, sidx); + let mut val = regs.get_n(sreg, sidx); regs.st0 = val.reshape(dreg.layout()); - regs.set(dreg, didx, val); + regs.set_n(dreg, didx, val); } MoveOp::CpyR(sreg, sidx, dreg, didx) => { - let mut val = regs.get(sreg, sidx); + let mut val = regs.get_n(sreg, sidx); regs.st0 = val.reshape(dreg.layout()); - regs.set(dreg, didx, val); + regs.set_n(dreg, didx, val); } MoveOp::SpyAR(sreg, sidx, dreg, didx) => { - let mut val1 = regs.get(sreg, sidx); - let mut val2 = regs.get(dreg, didx); + let mut val1 = regs.get_n(sreg, sidx); + let mut val2 = regs.get_n(dreg, didx); regs.st0 = val1.reshape(dreg.layout()) && val2.reshape(sreg.layout()); - regs.set(dreg, didx, val1); - regs.set(sreg, sidx, val2); + regs.set_n(dreg, didx, val1); + regs.set_n(sreg, sidx, val2); } MoveOp::CnvAF(sreg, sidx, dreg, didx) => { - let mut val = regs.get(sreg, sidx); + let mut val = regs.get_n(sreg, sidx); regs.st0 = val.reshape(dreg.layout()); - regs.set(dreg, didx, val); + regs.set_n(dreg, didx, val); } MoveOp::CnvFA(sreg, sidx, dreg, didx) => { - let mut val = regs.get(sreg, sidx); + let mut val = regs.get_n(sreg, sidx); regs.st0 = val.reshape(dreg.layout()); - regs.set(dreg, didx, val); + regs.set_n(dreg, didx, val); } } ExecStep::Next @@ -320,19 +493,70 @@ impl InstructionSet for CmpOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { + match self { + CmpOp::GtA(_, reg, idx1, idx2) => { + set![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] + } + CmpOp::LtA(_, reg, idx1, idx2) => { + set![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] + } + CmpOp::GtF(_, reg, idx1, idx2) => { + set![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] + } + CmpOp::LtF(_, reg, idx1, idx2) => { + set![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] + } + CmpOp::GtR(reg, idx1, idx2) => { + set![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] + } + CmpOp::LtR(reg, idx1, idx2) => { + set![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] + } + CmpOp::EqA(_, reg, idx1, idx2) => { + set![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] + } + CmpOp::EqF(_, reg, idx1, idx2) => { + set![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] + } + CmpOp::EqR(_, reg, idx1, idx2) => { + set![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] + } + + CmpOp::IfZA(reg, idx) | CmpOp::IfNA(reg, idx) => { + set![Reg::A(*reg, *idx)] + } + CmpOp::IfZR(reg, idx) | CmpOp::IfNR(reg, idx) => { + set![Reg::R(*reg, *idx)] + } + CmpOp::St(_, _, _) => { + set![] + } + CmpOp::StInv => set![], + } + } + + fn dst_regs(&self) -> HashSet { + match self { + CmpOp::St(_, reg, idx) => { + set![Reg::A(*reg, (*idx).into())] + } + _ => set![], + } + } + fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { match self { CmpOp::GtA(sign_flag, reg, idx1, idx2) => { - regs.st0 = - regs.get_both(reg, idx1, reg, idx2).map(|(val1, val2)| { - match bool::from(sign_flag) { - true => val1.into_signed().cmp(&val2.into_signed()), - false => val1.cmp(&val2), - } - }) == Some(Ordering::Greater); + regs.st0 = regs.get_n2(reg, idx1, reg, idx2).map(|(val1, val2)| { + match bool::from(sign_flag) { + true => val1.into_signed().cmp(&val2.into_signed()), + false => val1.cmp(&val2), + } + }) == Some(Ordering::Greater); } CmpOp::GtF(eq_flag, reg, idx1, idx2) => { - regs.st0 = regs.get_both(reg, idx1, reg, idx2).map(|(val1, val2)| { + regs.st0 = regs.get_n2(reg, idx1, reg, idx2).map(|(val1, val2)| { if *eq_flag == FloatEqFlag::Rounding { val1.rounding_cmp(&val2) } else { @@ -341,20 +565,19 @@ impl InstructionSet for CmpOp { }) == Some(Ordering::Greater); } CmpOp::GtR(reg, idx1, idx2) => { - regs.st0 = regs.get_both(reg, idx1, reg, idx2).map(|(val1, val2)| val1.cmp(&val2)) + regs.st0 = regs.get_n2(reg, idx1, reg, idx2).map(|(val1, val2)| val1.cmp(&val2)) == Some(Ordering::Greater); } CmpOp::LtA(sign_flag, reg, idx1, idx2) => { - regs.st0 = - regs.get_both(reg, idx1, reg, idx2).map(|(val1, val2)| { - match bool::from(sign_flag) { - true => val1.into_signed().cmp(&val2.into_signed()), - false => val1.cmp(&val2), - } - }) == Some(Ordering::Less); + regs.st0 = regs.get_n2(reg, idx1, reg, idx2).map(|(val1, val2)| { + match bool::from(sign_flag) { + true => val1.into_signed().cmp(&val2.into_signed()), + false => val1.cmp(&val2), + } + }) == Some(Ordering::Less); } CmpOp::LtF(eq_flag, reg, idx1, idx2) => { - regs.st0 = regs.get_both(reg, idx1, reg, idx2).map(|(val1, val2)| { + regs.st0 = regs.get_n2(reg, idx1, reg, idx2).map(|(val1, val2)| { if *eq_flag == FloatEqFlag::Rounding { val1.rounding_cmp(&val2) } else { @@ -363,18 +586,18 @@ impl InstructionSet for CmpOp { }) == Some(Ordering::Less); } CmpOp::LtR(reg, idx1, idx2) => { - regs.st0 = regs.get_both(reg, idx1, reg, idx2).map(|(val1, val2)| val1.cmp(&val2)) + regs.st0 = regs.get_n2(reg, idx1, reg, idx2).map(|(val1, val2)| val1.cmp(&val2)) == Some(Ordering::Less); } CmpOp::EqA(st, reg, idx1, idx2) => { regs.st0 = regs - .get_both(reg, idx1, reg, idx2) + .get_n2(reg, idx1, reg, idx2) .map(|(val1, val2)| val1 == val2) .unwrap_or(*st == NoneEqFlag::Equal); } CmpOp::EqF(eq_flag, reg, idx1, idx2) => { regs.st0 = regs - .get_both(reg, idx1, reg, idx2) + .get_n2(reg, idx1, reg, idx2) .map(|(val1, val2)| { if *eq_flag == FloatEqFlag::Rounding { val1.rounding_eq(&val2) @@ -386,21 +609,21 @@ impl InstructionSet for CmpOp { } CmpOp::EqR(st, reg, idx1, idx2) => { regs.st0 = regs - .get_both(reg, idx1, reg, idx2) + .get_n2(reg, idx1, reg, idx2) .map(|(val1, val2)| val1 == val2) .unwrap_or(*st == NoneEqFlag::Equal); } CmpOp::IfZA(reg, idx) => { - regs.st0 = regs.get(reg, idx).map(Number::is_zero).unwrap_or(false) + regs.st0 = regs.get_n(reg, idx).map(Number::is_zero).unwrap_or(false) } CmpOp::IfZR(reg, idx) => { - regs.st0 = regs.get(reg, idx).map(Number::is_zero).unwrap_or(false) + regs.st0 = regs.get_n(reg, idx).map(Number::is_zero).unwrap_or(false) } - CmpOp::IfNA(reg, idx) => regs.st0 = regs.get(reg, idx).is_none(), - CmpOp::IfNR(reg, idx) => regs.st0 = regs.get(reg, idx).is_none(), + CmpOp::IfNA(reg, idx) => regs.st0 = regs.get_n(reg, idx).is_none(), + CmpOp::IfNR(reg, idx) => regs.st0 = regs.get_n(reg, idx).is_none(), CmpOp::St(merge_flag, reg, idx) => { let st = Number::from(regs.st0 as u8); - let res = match (*regs.get(reg, idx), merge_flag) { + let res = match (*regs.get_n(reg, idx), merge_flag) { (None, _) | (_, MergeFlag::Set) => st, (Some(val), MergeFlag::Add) => { val.int_add(st, IntFlags { signed: false, wrap: false }).unwrap_or(val) @@ -408,7 +631,7 @@ impl InstructionSet for CmpOp { (Some(val), MergeFlag::And) => val & st, (Some(val), MergeFlag::Or) => val | st, }; - regs.set(reg, idx, Some(res)); + regs.set_n(reg, idx, Some(res)); } CmpOp::StInv => { regs.st0 = !regs.st0; @@ -424,6 +647,58 @@ impl InstructionSet for ArithmeticOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { + match self { + ArithmeticOp::Neg(reg, idx) | ArithmeticOp::Abs(reg, idx) => { + set![Reg::new(*reg, *idx)] + } + ArithmeticOp::Stp(reg, idx, _) => { + set![Reg::A(*reg, *idx)] + } + ArithmeticOp::AddA(_, reg, src, srcdst) + | ArithmeticOp::SubA(_, reg, src, srcdst) + | ArithmeticOp::MulA(_, reg, src, srcdst) + | ArithmeticOp::DivA(_, reg, src, srcdst) => { + set![Reg::A(*reg, *src), Reg::A(*reg, *srcdst)] + } + ArithmeticOp::AddF(_, reg, src, srcdst) + | ArithmeticOp::SubF(_, reg, src, srcdst) + | ArithmeticOp::MulF(_, reg, src, srcdst) + | ArithmeticOp::DivF(_, reg, src, srcdst) => { + set![Reg::F(*reg, *src), Reg::F(*reg, *srcdst)] + } + ArithmeticOp::Rem(reg1, src, reg2, srcdst) => { + set![Reg::A(*reg1, *src), Reg::A(*reg2, *srcdst)] + } + } + } + + fn dst_regs(&self) -> HashSet { + match self { + ArithmeticOp::Neg(reg, idx) | ArithmeticOp::Abs(reg, idx) => { + set![Reg::new(*reg, *idx)] + } + ArithmeticOp::Stp(reg, idx, _) => { + set![Reg::A(*reg, *idx)] + } + ArithmeticOp::AddA(_, reg, _src, srcdst) + | ArithmeticOp::SubA(_, reg, _src, srcdst) + | ArithmeticOp::MulA(_, reg, _src, srcdst) + | ArithmeticOp::DivA(_, reg, _src, srcdst) => { + set![Reg::A(*reg, *srcdst)] + } + ArithmeticOp::AddF(_, reg, _src, srcdst) + | ArithmeticOp::SubF(_, reg, _src, srcdst) + | ArithmeticOp::MulF(_, reg, _src, srcdst) + | ArithmeticOp::DivF(_, reg, _src, srcdst) => { + set![Reg::F(*reg, *srcdst)] + } + ArithmeticOp::Rem(_reg1, _src, reg2, srcdst) => { + set![Reg::A(*reg2, *srcdst)] + } + } + } + #[inline] fn complexity(&self) -> u64 { match self { @@ -446,65 +721,65 @@ impl InstructionSet for ArithmeticOp { fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { let is_some = match self { ArithmeticOp::Abs(reg, idx) => { - regs.set(reg, idx, regs.get(reg, idx).and_then(Number::abs)) + regs.set_n(reg, idx, regs.get_n(reg, idx).and_then(Number::abs)) } ArithmeticOp::AddA(flags, reg, src, srcdst) => { let res = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.int_add(val2, *flags)); - regs.set(reg, srcdst, res) + regs.set_n(reg, srcdst, res) } ArithmeticOp::AddF(flags, reg, src, srcdst) => { let res: Option = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.float_add(val2, *flags).into()); - regs.set(reg, srcdst, res) + regs.set_n(reg, srcdst, res) } ArithmeticOp::SubA(flags, reg, src, srcdst) => { let res = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.int_sub(val2, *flags)); - regs.set(reg, srcdst, res) + regs.set_n(reg, srcdst, res) } ArithmeticOp::SubF(flags, reg, src, srcdst) => { let res: Option = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.float_sub(val2, *flags).into()); - regs.set(reg, srcdst, res) + regs.set_n(reg, srcdst, res) } ArithmeticOp::MulA(flags, reg, src, srcdst) => { let res = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.int_mul(val2, *flags)); - regs.set(reg, srcdst, res) + regs.set_n(reg, srcdst, res) } ArithmeticOp::MulF(flags, reg, src, srcdst) => { let res: Option = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.float_mul(val2, *flags).into()); - regs.set(reg, srcdst, res) + regs.set_n(reg, srcdst, res) } ArithmeticOp::DivA(flags, reg, src, srcdst) => { let res = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.int_div(val2, *flags)); - regs.set(reg, srcdst, res) + regs.set_n(reg, srcdst, res) } ArithmeticOp::DivF(flags, reg, src, srcdst) => { let res: Option = regs - .get_both(reg, src, reg, srcdst) + .get_n2(reg, src, reg, srcdst) .and_then(|(val1, val2)| val1.float_div(val2, *flags).into()); - regs.set(reg, srcdst, res) && !res.map(Number::is_nan).unwrap_or(false) + regs.set_n(reg, srcdst, res) && !res.map(Number::is_nan).unwrap_or(false) } ArithmeticOp::Rem(reg1, idx1, reg2, idx2) => { let res = - regs.get_both(reg1, idx1, reg2, idx2).and_then(|(val1, val2)| val1.rem(val2)); - regs.set(reg2, idx2, res) + regs.get_n2(reg1, idx1, reg2, idx2).and_then(|(val1, val2)| val1.rem(val2)); + regs.set_n(reg2, idx2, res) } - ArithmeticOp::Stp(reg, idx, step) => regs.set( + ArithmeticOp::Stp(reg, idx, step) => regs.set_n( reg, idx, - regs.get(reg, idx).and_then(|val| { + regs.get_n(reg, idx).and_then(|val| { if step.as_i8() < 0 { let mut n = Number::from(-step.as_i8()); debug_assert!( @@ -523,7 +798,7 @@ impl InstructionSet for ArithmeticOp { }), ), ArithmeticOp::Neg(reg, idx) => { - regs.set(reg, idx, regs.get(reg, idx).and_then(Number::neg)) + regs.set_n(reg, idx, regs.get_n(reg, idx).and_then(Number::neg)) } }; regs.st0 = is_some; @@ -537,6 +812,80 @@ impl InstructionSet for BitwiseOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { + match self { + BitwiseOp::And(reg, idx1, idx2, _idx3) + | BitwiseOp::Or(reg, idx1, idx2, _idx3) + | BitwiseOp::Xor(reg, idx1, idx2, _idx3) => { + set![Reg::new(*reg, *idx1), Reg::new(*reg, *idx2)] + } + BitwiseOp::Not(reg, idx) => { + set![Reg::new(*reg, *idx)] + } + + BitwiseOp::Shl(a2, shift, reg, idx) => { + set![Reg::new(*a2, *shift), Reg::new(*reg, *idx)] + } + BitwiseOp::ShrA(_, a2, shift, reg, idx) => { + set![Reg::new(*a2, *shift), Reg::A(*reg, *idx)] + } + BitwiseOp::ShrR(a2, shift, reg, idx) => { + set![Reg::new(*a2, *shift), Reg::R(*reg, *idx)] + } + + BitwiseOp::Scl(a2, shift, reg, idx) => { + set![Reg::new(*a2, *shift), Reg::new(*reg, *idx)] + } + BitwiseOp::Scr(a2, shift, reg, idx) => { + set![Reg::new(*a2, *shift), Reg::new(*reg, *idx)] + } + + BitwiseOp::RevA(reg, idx) => { + set![Reg::A(*reg, *idx)] + } + BitwiseOp::RevR(reg, idx) => { + set![Reg::R(*reg, *idx)] + } + } + } + + fn dst_regs(&self) -> HashSet { + match self { + BitwiseOp::And(reg, _idx1, _idx2, idx3) + | BitwiseOp::Or(reg, _idx1, _idx2, idx3) + | BitwiseOp::Xor(reg, _idx1, _idx2, idx3) => { + set![Reg::new(*reg, *idx3)] + } + BitwiseOp::Not(reg, idx) => { + set![Reg::new(*reg, *idx)] + } + + BitwiseOp::Shl(_, _, reg, idx) => { + set![Reg::new(*reg, *idx)] + } + BitwiseOp::ShrA(_, _, _, reg, idx) => { + set![Reg::A(*reg, *idx)] + } + BitwiseOp::ShrR(_, _, reg, idx) => { + set![Reg::R(*reg, *idx)] + } + + BitwiseOp::Scl(_, _, reg, idx) => { + set![Reg::new(*reg, *idx)] + } + BitwiseOp::Scr(_, _, reg, idx) => { + set![Reg::new(*reg, *idx)] + } + + BitwiseOp::RevA(reg, idx) => { + set![Reg::A(*reg, *idx)] + } + BitwiseOp::RevR(reg, idx) => { + set![Reg::R(*reg, *idx)] + } + } + } + fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { fn shl(original: &[u8], shift: usize, n_bytes: usize) -> [u8; 1024] { let mut ret = [0u8; 1024]; @@ -579,11 +928,11 @@ impl InstructionSet for BitwiseOp { regs.op(reg, src1, reg, src2, reg, dst, BitXor::bitxor) } BitwiseOp::Not(reg, idx) => { - regs.set(reg, idx, !regs.get(reg, idx)); + regs.set_n(reg, idx, !regs.get_n(reg, idx)); } BitwiseOp::Shl(reg1, shift, reg2, srcdst) => match reg2 { RegAR::A(a) => { - let msb = regs.get(a, srcdst).unwrap_or_default()[a.bytes() - 1] & 0x80; + let msb = regs.get_n(a, srcdst).unwrap_or_default()[a.bytes() - 1] & 0x80; regs.st0 = msb == 0x80; regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Shl::shl) } @@ -601,7 +950,7 @@ impl InstructionSet for BitwiseOp { } }, BitwiseOp::ShrA(flag, reg1, shift, reg2, srcdst) => { - let res = regs.get_both(reg1, shift, reg2, srcdst).map(|(shift, val)| { + let res = regs.get_n2(reg1, shift, reg2, srcdst).map(|(shift, val)| { let lsb = val[0] & 1; regs.st0 = lsb == 1; if *flag == SignFlag::Signed { @@ -610,7 +959,7 @@ impl InstructionSet for BitwiseOp { val.shr(shift) } }); - regs.set(reg2, srcdst, res); + regs.set_n(reg2, srcdst, res); } BitwiseOp::ShrR(reg1, shift, reg2, srcdst) => { let shift = match reg1 { @@ -626,7 +975,7 @@ impl InstructionSet for BitwiseOp { } BitwiseOp::Scl(reg1, shift, reg2, srcdst) => match reg2 { RegAR::A(_) => { - let msb = regs.get(reg2, srcdst).unwrap_or_default()[reg2.bytes() - 1] & 0x80; + let msb = regs.get_n(reg2, srcdst).unwrap_or_default()[reg2.bytes() - 1] & 0x80; regs.st0 = msb == 0x80; regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Number::scl) } @@ -651,7 +1000,7 @@ impl InstructionSet for BitwiseOp { }, BitwiseOp::Scr(reg1, shift, reg2, srcdst) => match reg2 { RegAR::A(_) => { - let lsb = regs.get(reg2, srcdst).unwrap_or_default()[0] & 1; + let lsb = regs.get_n(reg2, srcdst).unwrap_or_default()[0] & 1; regs.st0 = lsb == 1; regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Number::scr) } @@ -675,7 +1024,7 @@ impl InstructionSet for BitwiseOp { } }, BitwiseOp::RevA(reg, idx) => { - regs.set(reg, idx, regs.get(reg, idx).map(Number::reverse_bits)); + regs.set_n(reg, idx, regs.get_n(reg, idx).map(Number::reverse_bits)); } BitwiseOp::RevR(reg, idx) => { if let Some(original) = regs.get_r_mut(*reg, idx) { @@ -694,6 +1043,105 @@ impl InstructionSet for BytesOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { + match self { + BytesOp::Put(_reg, _, _) => { + set![] + } + BytesOp::Swp(reg1, reg2) | BytesOp::Find(reg1, reg2) => { + set![Reg::S(*reg1), Reg::S(*reg2)] + } + BytesOp::Mov(reg1, _reg2) | BytesOp::Rev(reg1, _reg2) => { + set![Reg::S(*reg1)] + } + BytesOp::Fill(reg, offset1, offset2, value, _) => { + set![ + Reg::S(*reg), + Reg::A(RegA::A16, *offset1), + Reg::A(RegA::A16, *offset2), + Reg::A(RegA::A8, *value) + ] + } + BytesOp::Len(src, _reg, _dst) => { + set![Reg::S(*src)] + } + BytesOp::Cnt(src, byte, _cnt) => { + set![Reg::S(*src), Reg::new(RegA::A8, *byte)] + } + BytesOp::Eq(reg1, reg2) => { + set![Reg::S(*reg1), Reg::S(*reg2)] + } + BytesOp::Con(reg1, reg2, no, _offset, _len) => { + set![Reg::S(*reg1), Reg::S(*reg2), Reg::A(RegA::A16, *no),] + } + BytesOp::Extr(src, _dst, _index, offset) => { + set![Reg::S(*src), Reg::new(RegA::A16, *offset)] + } + BytesOp::Inj(src1, src2, index, offset) => { + set![Reg::S(*src1), Reg::new(*src2, *index), Reg::new(RegA::A16, *offset)] + } + BytesOp::Join(src1, src2, _dst) => { + set![Reg::S(*src1), Reg::S(*src2)] + } + BytesOp::Splt(_flag, offset, src, _dst1, _dst2) => { + set![Reg::A(RegA::A16, *offset), Reg::S(*src)] + } + BytesOp::Ins(_flag, offset, src, _dst) => { + set![Reg::A(RegA::A16, *offset), Reg::S(*src)] + } + BytesOp::Del(_flag, reg1, offset1, reg2, offset2, _flag1, _flag2, src, _dst) => { + set![Reg::new(*reg1, *offset1), Reg::new(*reg2, *offset2), Reg::S(*src)] + } + } + } + + fn dst_regs(&self) -> HashSet { + match self { + BytesOp::Put(reg, _, _) => { + set![Reg::S(*reg)] + } + BytesOp::Swp(reg1, reg2) | BytesOp::Find(reg1, reg2) => { + set![Reg::S(*reg1), Reg::S(*reg2)] + } + BytesOp::Mov(_reg1, reg2) | BytesOp::Rev(_reg1, reg2) => { + set![Reg::S(*reg2)] + } + BytesOp::Fill(reg, _offset1, _offset2, _value, _) => { + set![Reg::S(*reg)] + } + BytesOp::Len(_src, reg, dst) => { + set![Reg::A(*reg, *dst)] + } + BytesOp::Cnt(_src, _byte, cnt) => { + set![Reg::new(RegA::A16, *cnt)] + } + BytesOp::Eq(_reg1, _reg2) => { + set![] + } + BytesOp::Con(_reg1, _reg2, _no, offset, len) => { + set![Reg::A(RegA::A16, *offset), Reg::A(RegA::A16, *len)] + } + BytesOp::Extr(_src, dst, index, _offset) => { + set![Reg::new(*dst, *index)] + } + BytesOp::Inj(src1, _src2, _index, _offset) => { + set![Reg::S(*src1)] + } + BytesOp::Join(_src1, _src2, dst) => { + set![Reg::S(*dst)] + } + BytesOp::Splt(_flag, _offset, _src, dst1, dst2) => { + set![Reg::S(*dst1), Reg::S(*dst2)] + } + BytesOp::Ins(_flag, _offset, _src, dst) => { + set![Reg::S(*dst)] + } + BytesOp::Del(_flag, _reg1, _offset1, _reg2, _offset2, _flag1, _flag2, _src, dst) => { + set![Reg::S(*dst)] + } + } + } + #[inline] fn complexity(&self) -> u64 { 5 } @@ -745,12 +1193,12 @@ impl InstructionSet for BytesOp { if !reg.int_layout().fits_usize(len as usize) { return None; } - regs.set(reg, dst, len as u32); + regs.set_n(reg, dst, len as u32); Some(()) }; f().unwrap_or_else(|| { regs.st0 = false; - regs.set(reg, dst, MaybeNumber::none()); + regs.set_n(reg, dst, MaybeNumber::none()); }); } BytesOp::Cnt(src, byte, dst) => { @@ -761,12 +1209,12 @@ impl InstructionSet for BytesOp { if !RegA::A16.int_layout().fits_usize(count) { return None; } - regs.set(RegA::A16, dst, count as u32); + regs.set_n(RegA::A16, dst, count as u32); Some(()) }; f().unwrap_or_else(|| { regs.st0 = false; - regs.set(RegA::A16, dst, MaybeNumber::none()); + regs.set_n(RegA::A16, dst, MaybeNumber::none()); }); } BytesOp::Eq(reg1, reg2) => { @@ -780,17 +1228,17 @@ impl InstructionSet for BytesOp { } BytesOp::Find(reg1, reg2) => { let mut f = || -> Option<()> { - let (s1, s2) = regs.get_both_s(*reg1, *reg2)?; + let (s1, s2) = regs.get_s2(*reg1, *reg2)?; let r1 = s1.as_ref(); let r2 = s2.as_ref(); let count = r1.windows(r2.len()).filter(|r1| *r1 == r2).count(); assert!(count <= u16::MAX as usize); - regs.set(RegA::A16, Reg32::Reg0, count as u16); + regs.set_n(RegA::A16, Reg32::Reg0, count as u16); Some(()) }; f().unwrap_or_else(|| { regs.st0 = false; - regs.set(RegA::A16, Reg32::Reg0, MaybeNumber::none()); + regs.set_n(RegA::A16, Reg32::Reg0, MaybeNumber::none()); }) } BytesOp::Rev(reg1, reg2) => { @@ -827,14 +1275,14 @@ impl InstructionSet for BytesOp { (Some((b, _)), None) => (b, size - b), _ => return None, }; - regs.set(RegA::A16, offset_dst, offset); - regs.set(RegA::A16, len_dst, len); + regs.set_n(RegA::A16, offset_dst, offset); + regs.set_n(RegA::A16, len_dst, len); Some(()) }; f().unwrap_or_else(|| { regs.st0 = false; - regs.set(RegA::A16, offset_dst, MaybeNumber::none()); - regs.set(RegA::A16, len_dst, MaybeNumber::none()); + regs.set_n(RegA::A16, offset_dst, MaybeNumber::none()); + regs.set_n(RegA::A16, len_dst, MaybeNumber::none()); }) } BytesOp::Extr(src, dst, index, offset) => { @@ -851,18 +1299,18 @@ impl InstructionSet for BytesOp { let num = Number::from_slice( ®s.get_s(*src)?.as_ref()[offset as usize..end as usize], ); - regs.set(dst, index, num); + regs.set_n(dst, index, num); Some(()) }; f().unwrap_or_else(|| { regs.st0 = false; - regs.set(dst, index, MaybeNumber::none()); + regs.set_n(dst, index, MaybeNumber::none()); }) } BytesOp::Inj(src, dst, index, offset) => { let mut f = || -> Option<()> { let mut s = regs.get_s(*src)?.clone(); - let val = regs.get(dst, index).map(|v| v)?; + let val = regs.get_n(dst, index).map(|v| v)?; let offset = regs.a16[*offset as u8 as usize]?; let end = offset.saturating_add(dst.layout().bytes() - 1); s.adjust_len(end); @@ -872,12 +1320,12 @@ impl InstructionSet for BytesOp { }; f().unwrap_or_else(|| { regs.st0 = false; - regs.set(dst, index, MaybeNumber::none()); + regs.set_n(dst, index, MaybeNumber::none()); }) } BytesOp::Join(src1, src2, dst) => { let mut f = || -> Option<()> { - let (s1, s2) = regs.get_both_s(*src1, *src2)?; + let (s1, s2) = regs.get_s2(*src1, *src2)?; if s1.len() as usize + s2.len() as usize > u16::MAX as usize { return None; } @@ -918,6 +1366,22 @@ impl InstructionSet for DigestOp { set } + fn src_regs(&self) -> HashSet { + match self { + DigestOp::Ripemd(src, _dst) + | DigestOp::Sha256(src, _dst) + | DigestOp::Sha512(src, _dst) => set![Reg::S(*src)], + } + } + + fn dst_regs(&self) -> HashSet { + match self { + DigestOp::Ripemd(_src, dst) => set![Reg::new(RegR::R160, *dst)], + DigestOp::Sha256(_src, dst) => set![Reg::new(RegR::R256, *dst)], + DigestOp::Sha512(_src, dst) => set![Reg::new(RegR::R512, *dst)], + } + } + #[inline] fn complexity(&self) -> u64 { 100 } @@ -933,19 +1397,19 @@ impl InstructionSet for DigestOp { hash.reverse(); hash }); - regs.set(RegR::R160, dst, hash); + regs.set_n(RegR::R160, dst, hash); } DigestOp::Sha256(src, dst) => { let s = regs.get_s(*src); none = s.is_none(); let hash: Option<[u8; 32]> = s.map(|s| sha2::Sha256::digest(s.as_ref()).into()); - regs.set(RegR::R256, dst, hash); + regs.set_n(RegR::R256, dst, hash); } DigestOp::Sha512(src, dst) => { let s = regs.get_s(*src); none = s.is_none(); let hash: Option<[u8; 64]> = s.map(|s| sha2::Sha512::digest(s.as_ref()).into()); - regs.set(RegR::R512, dst, hash); + regs.set_n(RegR::R512, dst, hash); } } if none { @@ -970,6 +1434,43 @@ impl InstructionSet for Secp256k1Op { set } + fn src_regs(&self) -> HashSet { + match self { + Secp256k1Op::Gen(src, _dst) => { + set![Reg::R(RegR::R256, *src)] + } + Secp256k1Op::Mul(RegBlockAR::A, scal, src, _dst) => { + set![Reg::A(RegA::A256, *scal), Reg::R(RegR::R512, *src)] + } + Secp256k1Op::Mul(RegBlockAR::R, scal, src, _dst) => { + set![Reg::R(RegR::R256, *scal), Reg::R(RegR::R512, *src)] + } + Secp256k1Op::Add(src, srcdst) => { + set![Reg::R(RegR::R512, *src), Reg::new(RegR::R512, *srcdst)] + } + Secp256k1Op::Neg(src, _dst) => { + set![Reg::R(RegR::R512, *src)] + } + } + } + + fn dst_regs(&self) -> HashSet { + match self { + Secp256k1Op::Gen(_src, dst) => { + set![Reg::new(RegR::R512, *dst)] + } + Secp256k1Op::Mul(_, _, _src, dst) => { + set![Reg::R(RegR::R512, *dst)] + } + Secp256k1Op::Add(_src, srcdst) => { + set![Reg::new(RegR::R512, *srcdst)] + } + Secp256k1Op::Neg(_src, dst) => { + set![Reg::new(RegR::R512, *dst)] + } + } + } + #[inline] fn complexity(&self) -> u64 { 1000 } @@ -985,7 +1486,7 @@ impl InstructionSet for Secp256k1Op { match self { Secp256k1Op::Gen(src, dst) => { let res = regs - .get(RegR::R256, src) + .get_n(RegR::R256, src) .and_then(|mut src| { let src = src.as_mut(); // little endian to big endian @@ -996,15 +1497,15 @@ impl InstructionSet for Secp256k1Op { .as_ref() .map(PublicKey::serialize_uncompressed) .map(|pk| Number::from_slice(&pk[1..])); - regs.set(RegR::R512, dst, res); + regs.set_n(RegR::R512, dst, res); } Secp256k1Op::Mul(block, scal, src, dst) => { let reg = block.into_reg(256).expect("register set does not match standard"); let res = regs - .get(reg, scal) + .get_n(reg, scal) .and_then(|scal| { - regs.get(RegR::R512, src) + regs.get_n(RegR::R512, src) .and_then(|val| { let mut pk = [4u8; 65]; pk[1..].copy_from_slice(val.as_ref()); @@ -1021,19 +1522,19 @@ impl InstructionSet for Secp256k1Op { .as_ref() .map(PublicKey::serialize_uncompressed) .map(|pk| Number::from_slice(&pk[1..])); - regs.set(RegR::R512, dst, res); + regs.set_n(RegR::R512, dst, res); } Secp256k1Op::Add(src, srcdst) => { let res = regs - .get(RegR::R512, src) + .get_n(RegR::R512, src) .and_then(|val| { let mut pk1 = [4u8; 65]; pk1[1..].copy_from_slice(val.as_ref()); PublicKey::from_slice(&pk1).ok() }) .and_then(|pk1| { - regs.get(RegR::R512, srcdst).and_then(|val| { + regs.get_n(RegR::R512, srcdst).and_then(|val| { let mut pk2 = [4u8; 65]; pk2[1..].copy_from_slice(val.as_ref()); PublicKey::from_slice(&pk2).ok().map(|pk2| (pk1, pk2)) @@ -1043,12 +1544,12 @@ impl InstructionSet for Secp256k1Op { .as_ref() .map(PublicKey::serialize_uncompressed) .map(|pk| Number::from_slice(&pk[1..])); - regs.set(RegR::R512, srcdst, res); + regs.set_n(RegR::R512, srcdst, res); } Secp256k1Op::Neg(src, dst) => { let res = regs - .get(RegR::R512, src) + .get_n(RegR::R512, src) .and_then(|val| { let mut pk = [4u8; 65]; pk[1..].copy_from_slice(&val[..]); @@ -1058,7 +1559,7 @@ impl InstructionSet for Secp256k1Op { .as_ref() .map(PublicKey::serialize_uncompressed) .map(|pk| Number::from_slice(&pk[1..])); - regs.set(RegR::R512, dst, res); + regs.set_n(RegR::R512, dst, res); } } ExecStep::Next @@ -1080,6 +1581,43 @@ impl InstructionSet for Curve25519Op { set } + fn src_regs(&self) -> HashSet { + match self { + Curve25519Op::Gen(src, _dst) => { + set![Reg::R(RegR::R256, *src)] + } + Curve25519Op::Mul(RegBlockAR::A, scal, src, _dst) => { + set![Reg::A(RegA::A256, *scal), Reg::R(RegR::R512, *src)] + } + Curve25519Op::Mul(RegBlockAR::R, scal, src, _dst) => { + set![Reg::R(RegR::R256, *scal), Reg::R(RegR::R512, *src)] + } + Curve25519Op::Add(src1, src2, _dst, _) => { + set![Reg::R(RegR::R512, *src1), Reg::new(RegR::R512, *src2)] + } + Curve25519Op::Neg(src, _dst) => { + set![Reg::R(RegR::R512, *src)] + } + } + } + + fn dst_regs(&self) -> HashSet { + match self { + Curve25519Op::Gen(_src, dst) => { + set![Reg::new(RegR::R512, *dst)] + } + Curve25519Op::Mul(_, _, _src, dst) => { + set![Reg::R(RegR::R512, *dst)] + } + Curve25519Op::Add(_src1, _src2, dst, _) => { + set![Reg::new(RegR::R512, *dst)] + } + Curve25519Op::Neg(_src, dst) => { + set![Reg::new(RegR::R512, *dst)] + } + } + } + #[inline] fn complexity(&self) -> u64 { 1000 } @@ -1089,66 +1627,8 @@ impl InstructionSet for Curve25519Op { } #[cfg(feature = "curve25519")] - fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { - use amplify::num::u256; - use curve25519_dalek::constants::ED25519_BASEPOINT_POINT; - use curve25519_dalek::scalar::Scalar; - - let get_scalar = |src: Number| { - let mut scal = [0u8; 32]; - scal.copy_from_slice(&src.as_ref()[..32]); - Scalar::from_bits(scal) - }; - - let from_scalar = |scal: Scalar| { - let mut n = [0u8; 64]; - n[..32].copy_from_slice(scal.as_bytes()); - n[32..].copy_from_slice((ED25519_BASEPOINT_POINT * scal).compress().as_bytes()); - Number::from_slice(n) - }; - - match self { - Curve25519Op::Gen(src, dst) => { - let res = regs.get(RegR::R256, src).map(get_scalar).map(from_scalar); - regs.set(RegR::R512, dst, res); - } - Curve25519Op::Mul(block, scal, src, dst) => { - let reg = block.into_reg(256).expect("register set does not match standard"); - let lhs = regs.get(reg, scal).map(get_scalar); - let rhs = regs.get(reg, src).map(get_scalar); - let res = lhs.zip(rhs).map(|(lhs, rhs)| lhs * rhs).map(from_scalar); - regs.set(RegR::R512, dst, res); - } - Curve25519Op::Add(lhs, rhs, dst, overflow) => { - let lhs = regs - .get(RegR::R512, lhs) - .map(get_scalar) - .map(|s| u256::from_le_bytes(s.to_bytes())); - let rhs = regs - .get(RegR::R512, rhs) - .map(get_scalar) - .map(|s| u256::from_le_bytes(s.to_bytes())); - let res = lhs - .zip(rhs) - .and_then(|(lhs, rhs)| { - let scal = Scalar::from_bits((lhs + rhs).to_le_bytes()); - match !*overflow && !scal.is_canonical() { - true => { - regs.st0 = false; - None - } - false => Some(scal.reduce()), - } - }) - .map(from_scalar); - regs.set(RegR::R512, dst, res); - } - Curve25519Op::Neg(src, dst) => { - let res = regs.get(RegR::R512, src).map(get_scalar).map(|s| -s).map(from_scalar); - regs.set(RegR::R512, dst, res); - } - } - ExecStep::Next + fn exec(&self, _regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { + todo!("implement Curve256 operations") } } @@ -1158,6 +1638,10 @@ impl InstructionSet for ReservedOp { #[inline] fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn src_regs(&self) -> HashSet { set![] } + + fn dst_regs(&self) -> HashSet { set![] } + fn exec(&self, regs: &mut CoreRegs, site: LibSite, ctx: &()) -> ExecStep { ControlFlowOp::Fail.exec(regs, site, ctx) } @@ -1196,8 +1680,8 @@ mod tests { lib_site, &(), ); - assert_eq!(register.get(RegA::A16, Reg32::Reg1).unwrap(), Number::from(0u16)); - assert_eq!(register.get(RegA::A16, Reg32::Reg2).unwrap(), Number::from(5u16)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg1).unwrap(), Number::from(0u16)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg2).unwrap(), Number::from(5u16)); assert!(register.st0); // banana (1st fragment) PutOp::PutA(RegA::A16, Reg32::Reg0, MaybeNumber::from(1).into()).exec( @@ -1210,8 +1694,8 @@ mod tests { lib_site, &(), ); - assert_eq!(register.get(RegA::A16, Reg32::Reg1).unwrap(), Number::from(6u16)); - assert_eq!(register.get(RegA::A16, Reg32::Reg2).unwrap(), Number::from(6u16)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg1).unwrap(), Number::from(6u16)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg2).unwrap(), Number::from(6u16)); assert!(register.st0); // kiwi (2nd fragment) PutOp::PutA(RegA::A16, Reg32::Reg0, MaybeNumber::from(2).into()).exec( @@ -1224,8 +1708,8 @@ mod tests { lib_site, &(), ); - assert_eq!(register.get(RegA::A16, Reg32::Reg1).unwrap(), Number::from(13u16)); - assert_eq!(register.get(RegA::A16, Reg32::Reg2).unwrap(), Number::from(4u16)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg1).unwrap(), Number::from(13u16)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg2).unwrap(), Number::from(4u16)); assert!(register.st0); // no 3rd fragment PutOp::PutA(RegA::A16, Reg32::Reg0, MaybeNumber::from(3).into()).exec( @@ -1238,8 +1722,8 @@ mod tests { lib_site, &(), ); - assert_eq!(register.get(RegA::A16, Reg32::Reg1), MaybeNumber::none()); - assert_eq!(register.get(RegA::A16, Reg32::Reg2), MaybeNumber::none()); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg1), MaybeNumber::none()); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg2), MaybeNumber::none()); assert!(!register.st0); let s1 = "aaa".as_bytes(); @@ -1264,8 +1748,8 @@ mod tests { lib_site, &(), ); - assert_eq!(register.get(RegA::A16, Reg32::Reg1), MaybeNumber::none()); - assert_eq!(register.get(RegA::A16, Reg32::Reg2), MaybeNumber::none()); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg1), MaybeNumber::none()); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg2), MaybeNumber::none()); assert!(!register.st0); ControlFlowOp::Succ.exec(&mut register, lib_site, &()); @@ -1291,8 +1775,8 @@ mod tests { lib_site, &(), ); - assert_eq!(register.get(RegA::A16, Reg32::Reg1).unwrap(), Number::from(0u16)); - assert_eq!(register.get(RegA::A16, Reg32::Reg2).unwrap(), Number::from(u16::MAX)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg1).unwrap(), Number::from(0u16)); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg2).unwrap(), Number::from(u16::MAX)); assert!(register.st0); PutOp::PutA(RegA::A16, Reg32::Reg0, MaybeNumber::from(1).into()).exec( &mut register, @@ -1304,8 +1788,8 @@ mod tests { lib_site, &(), ); - assert_eq!(register.get(RegA::A16, Reg32::Reg1), MaybeNumber::none()); - assert_eq!(register.get(RegA::A16, Reg32::Reg2), MaybeNumber::none()); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg1), MaybeNumber::none()); + assert_eq!(register.get_n(RegA::A16, Reg32::Reg2), MaybeNumber::none()); assert!(!register.st0); } @@ -1338,7 +1822,7 @@ mod tests { lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); } #[test] @@ -1373,7 +1857,7 @@ mod tests { lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); } #[test] @@ -1394,15 +1878,15 @@ mod tests { lib_site, &(), ); - assert_eq!(false, register.st0); + assert!(!register.st0); ControlFlowOp::Succ.exec(&mut register, lib_site, &()); - assert_eq!(true, register.st0); + assert!(register.st0); CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg0, Reg32::Reg2).exec( &mut register, lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); PutOp::PutR(RegR::R256, Reg32::Reg4, MaybeNumber::from(5u8).into()).exec( &mut register, lib_site, @@ -1422,9 +1906,10 @@ mod tests { lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); } + /* TODO: Enable after curve25519 re-implementation #[test] #[cfg(feature = "curve25519")] fn curve25519_mul_test() { @@ -1452,18 +1937,18 @@ mod tests { &(), ); Curve25519Op::Gen(Reg32::Reg2, Reg8::Reg2).exec(&mut register, lib_site, &()); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg1, Reg32::Reg2).exec( + CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg1, Reg32::Reg2).exec( &mut register, lib_site, &(), ); - assert_eq!(true, register.st0); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg0, Reg32::Reg2).exec( + assert!(register.st0); + CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg0, Reg32::Reg2).exec( &mut register, lib_site, &(), ); - assert_eq!(false, register.st0); + assert!(!register.st0); } #[test] @@ -1494,12 +1979,12 @@ mod tests { lib_site, &(), ); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg2, Reg32::Reg3).exec( + CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg2, Reg32::Reg3).exec( &mut register, lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); } #[test] @@ -1528,28 +2013,28 @@ mod tests { lib_site, &(), ); - Curve25519Op::Gen(Reg32::Reg0, Reg8::Reg0).exec(&mut register, lib_site, &()); + Curve25519Op::Gen(Reg32::Reg0, Reg8::Reg7).exec(&mut register, lib_site, &()); Curve25519Op::Gen(Reg32::Reg1, Reg8::Reg1).exec(&mut register, lib_site, &()); Curve25519Op::Gen(Reg32::Reg2, Reg8::Reg2).exec(&mut register, lib_site, &()); - Curve25519Op::Add(Reg32::Reg0, Reg32::Reg1, Reg32::Reg3, false).exec( + Curve25519Op::Add(Reg32::Reg7, Reg32::Reg1, Reg32::Reg3, false).exec( &mut register, lib_site, &(), ); - assert_eq!(false, register.st0); + assert!(!register.st0); ControlFlowOp::Succ.exec(&mut register, lib_site, &()); Curve25519Op::Add(Reg32::Reg0, Reg32::Reg1, Reg32::Reg3, true).exec( &mut register, lib_site, &(), ); - assert_eq!(true, register.st0); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg2, Reg32::Reg3).exec( + assert!(register.st0); + CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg2, Reg32::Reg3).exec( &mut register, lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); } #[test] @@ -1565,20 +2050,20 @@ mod tests { Curve25519Op::Gen(Reg32::Reg0, Reg8::Reg0).exec(&mut register, lib_site, &()); Curve25519Op::Neg(Reg32::Reg0, Reg8::Reg1).exec(&mut register, lib_site, &()); Curve25519Op::Neg(Reg32::Reg1, Reg8::Reg2).exec(&mut register, lib_site, &()); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg0, Reg32::Reg1).exec( + CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg0, Reg32::Reg1).exec( &mut register, lib_site, &(), ); - assert_eq!(false, register.st0); + assert!(!register.st0); ControlFlowOp::Succ.exec(&mut register, lib_site, &()); - assert_eq!(true, register.st0); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg0, Reg32::Reg2).exec( + assert!(register.st0); + CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg0, Reg32::Reg2).exec( &mut register, lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); PutOp::PutR(RegR::R256, Reg32::Reg4, MaybeNumber::from(5u8).into()).exec( &mut register, lib_site, @@ -1597,11 +2082,12 @@ mod tests { lib_site, &(), ); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg4, Reg32::Reg6).exec( + CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg4, Reg32::Reg6).exec( &mut register, lib_site, &(), ); - assert_eq!(true, register.st0); + assert!(register.st0); } + */ } diff --git a/src/isa/instr.rs b/src/isa/instr.rs index 734b906..e46baed 100644 --- a/src/isa/instr.rs +++ b/src/isa/instr.rs @@ -915,14 +915,14 @@ pub enum Curve25519Op { /// Generates new elliptic curve point value saved into destination /// register in `r512` set using scalar value from the source `r256` /// register - #[display("edgen r256{0},r512{1}")] + #[display("edgen r256{0},r256{1}")] Gen( /** Register containing scalar */ Reg32, /** Destination register to put G * scalar */ Reg8, ), /// Multiplies elliptic curve point on a scalar - #[display("edmul {0}256{1},r512{2},r512{3}")] + #[display("edmul {0}256{1},r256{2},r256{3}")] Mul( /** Use `a` or `r` register as scalar source */ RegBlockAR, /** Scalar register index */ Reg32, @@ -931,7 +931,7 @@ pub enum Curve25519Op { ), /// Adds two elliptic curve points - #[display("edadd r512{0},r512{1},r512{2},{3}")] + #[display("edadd r512{0},r256{1},r256{2},{3}")] Add( /** Source 1 */ Reg32, /** Source 2 */ Reg32, @@ -940,6 +940,6 @@ pub enum Curve25519Op { ), /// Negates elliptic curve point - #[display("edneg r512{0},r512{1}")] + #[display("edneg r256{0},r256{1}")] Neg(/** Register hilding EC point to negate */ Reg32, /** Destination register */ Reg8), } diff --git a/src/isa/macros.rs b/src/isa/macros.rs new file mode 100644 index 0000000..7a3e1a2 --- /dev/null +++ b/src/isa/macros.rs @@ -0,0 +1,1387 @@ +// AluVM Assembler +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2021-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. +// Copyright (C) 2023 UBIDECO Institute. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Macro compiler for AluVM assembler. +/// +/// # Example +/// +/// ``` +/// # use aluvm::aluasm; +/// # use aluvm::{Prog, Vm}; +/// # use aluvm::library::Lib; +/// # use aluvm::isa::Instr; +/// +/// let code = aluasm! { +/// clr r1024[5] ; +/// put a16[8],378 ; +/// putif r128[5],0xaf67937b5498dc ; +/// swp a8[1],a8[2] ; +/// swp f256[8],f256[7] ; +/// dup a256[1],a256[7] ; +/// mov a16[1],a16[2] ; +/// mov r256[8],r256[7] ; +/// cpy a256[1],a256[7] ; +/// ret ; +/// jmp 0 ; +/// }; +/// +/// let lib = Lib::assemble(&code).unwrap(); +/// let program = Prog::::new(lib); +/// let mut vm = Vm::::new(); +/// match vm.run(&program, &()) { +/// true => println!("success"), +/// false => println!("failure"), +/// } +/// ``` +#[macro_export] +macro_rules! aluasm { + ($( $tt:tt )+) => {{ #[allow(unused_imports)] { + use ::aluvm::isa::ReservedOp; + $crate::aluasm_isa! { ReservedOp => $( $tt )+ } + } }}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! aluasm_isa { + ($isa:ty => $( $tt:tt )+) => {{ + use ::std::boxed::Box; + + use ::aluvm::isa::{ + ArithmeticOp, BitwiseOp, BytesOp, CmpOp, ControlFlowOp, DigestOp, ExtendFlag, FloatEqFlag, Instr, IntFlags, + MergeFlag, MoveOp, PutOp, RoundingFlag, Secp256k1Op, SignFlag, NoneEqFlag + }; + use ::aluvm::reg::{ + Reg16, Reg32, Reg8, RegA, RegA2, RegBlockAFR, RegBlockAR, RegF, RegR, RegS, + NumericRegister, + }; + use ::aluvm::library::LibSite; + use ::aluvm::data::{ByteStr, Number, MaybeNumber, Step}; + + let mut code: Vec> = vec![]; + #[allow(unreachable_code)] { + $crate::aluasm_inner! { code => $( $tt )+ } + } + code + }} +} + +#[doc(hidden)] +#[macro_export] +macro_rules! aluasm_inner { + { $code:ident => } => { }; + { $code:ident => $op:ident ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $arg:literal @ $lib:ident ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg @ $lib }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $( $arg:literal ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $( $arg:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident . $flag:ident $( $arg:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op . $flag $( $arg ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $( $arg:ident [ $idx:literal ] ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg [ $idx ] ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident . $flag:ident $( $arg:ident [ $idx:literal ] ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op . $flag $( $arg [ $idx ] ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $arglit:literal, $arg:ident [ $idx:literal ] ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arglit, $arg [ $idx ] }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident . $flag:ident $arglit:literal, $arg:ident [ $idx:literal ] ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op . $flag $arglit, $arg [ $idx ] }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $arglit1:literal, $arglit2:literal, $arg:ident [ $idx:literal ] ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arglit1, $arglit2, $arg [ $idx ] }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident . $flag:ident $arglit1:literal, $arglit2:literal $arg:ident [ $idx:literal ] ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op . $flag $arglit1, $arglit2, $arg [ $idx ] }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $arg:ident [ $idx:literal ] , $arglit:literal ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg [ $idx ] , $arglit }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident . $flag:ident $arg:ident [ $idx:literal ], $arglit:expr ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op . $flag $arg [ $idx ], $arglit }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! instr { + (fail) => { + Instr::ControlFlow(ControlFlowOp::Fail) + }; + (succ) => { + Instr::ControlFlow(ControlFlowOp::Succ) + }; + (jmp $offset:literal) => { + Instr::ControlFlow(ControlFlowOp::Jmp($offset)) + }; + (jif $offset:literal) => { + Instr::ControlFlow(ControlFlowOp::Jif($offset)) + }; + (routine $offset:literal) => { + Instr::ControlFlow(ControlFlowOp::Reutine($offset)) + }; + (call $offset:literal @ $lib:ident) => { + Instr::ControlFlow(ControlFlowOp::Call(LibSite::with( + $offset, + stringify!($lib).parse().expect("wrong library reference"), + ))) + }; + (exec $offset:literal @ $lib:ident) => { + Instr::ControlFlow(ControlFlowOp::Exec(LibSite::with( + $offset, + stringify!($lib).parse().expect("wrong library reference"), + ))) + }; + (ret) => { + Instr::ControlFlow(ControlFlowOp::Ret) + }; + + (clr $reg:ident[$idx:literal]) => { + Instr::Put($crate::_reg_sfx!(PutOp, Clr, $reg)( + $crate::_reg_ty!(Reg, $reg), + $crate::_reg_idx!($idx), + )) + }; + + (extr s16[$idx:literal], $regr:ident[$regr_idx:literal],a16[$offset_idx:literal]) => { + Instr::Bytes(BytesOp::Extr( + RegS::from($idx), + $crate::_reg_ty!(Reg, $regr), + $crate::_reg_idx16!($regr_idx), + $crate::_reg_idx16!($offset_idx), + )) + }; + (put s16[$idx:literal], $val:literal) => {{ + Instr::Bytes(BytesOp::Put(RegS::from($idx), Box::new(ByteStr::with(&$val)), false)) + }}; + (fill.e s16[$idx0:literal],a16[$idx1:literal],a16[$idx2:literal],a8[$idx3:literal]) => {{ + Instr::Bytes(BytesOp::Fill( + RegS::from($idx0), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + $crate::_reg_idx!($idx3), + ExtendFlag::Extend, + )) + }}; + (fill.f s16[$idx0:literal],a16[$idx1:literal],a16[$idx2:literal],a8[$idx3:literal]) => {{ + Instr::Bytes(BytesOp::Fill( + RegS::from($idx0), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + $crate::_reg_idx!($idx3), + ExtendFlag::Fail, + )) + }}; + (len s16[$s_idx:literal], $rega:ident[$rega_idx:literal]) => {{ + Instr::Bytes(BytesOp::Len( + RegS::from($s_idx), + $crate::_reg_tya!(Reg, $rega), + $crate::_reg_idx!($rega_idx), + )) + }}; + (cnt s16[$s_idx:literal],a8[$byte_idx:literal],a16[$dst_idx:literal]) => {{ + Instr::Bytes(BytesOp::Cnt( + RegS::from($s_idx), + $crate::_reg_idx16!($byte_idx), + $crate::_reg_idx16!($dst_idx), + )) + }}; + ( + con s16[$src1_idx:literal],s16[$src2_idx:literal],a16[$frag_idx:literal],a16[$offset_dst_idx:literal],a16[$len_dst_idx:literal] + ) => {{ + Instr::Bytes(BytesOp::Con( + RegS::from($src1_idx), + RegS::from($src2_idx), + $crate::_reg_idx!($frag_idx), + $crate::_reg_idx!($offset_dst_idx), + $crate::_reg_idx!($len_dst_idx), + )) + }}; + (find s16[$str_idx:literal],s16[$fragment_idx:literal],a16[$should_be_0:literal]) => {{ + assert_eq!(0, $should_be_0); + Instr::Bytes(BytesOp::Find(RegS::from($str_idx), RegS::from($fragment_idx))) + }}; + (rev s16[$src_idx:literal],s16[$dst_idx:literal]) => {{ + Instr::Bytes(BytesOp::Rev(RegS::from($src_idx), RegS::from($dst_idx))) + }}; + (put $reg:ident[$idx:literal], $val:literal) => {{ + let s = stringify!($val); + let mut num = s.parse::().expect(&format!("invalid number literal `{}`", s)); + let reg = $crate::_reg_ty!(Reg, $reg); + num.reshape(reg.layout()); + Instr::Put($crate::_reg_sfx!(PutOp, Put, $reg)(reg, $crate::_reg_idx!($idx), Box::new(num))) + }}; + (putif $reg:ident[$idx:literal], $val:literal) => {{ + let s = stringify!($val); + let mut num = s.parse::().expect(&format!("invalid number literal `{}`", s)); + let reg = $crate::_reg_ty!(Reg, $reg); + num.reshape(reg.layout()); + Instr::Put($crate::_reg_sfx!(PutOp, PutIf, $reg)( + reg, + $crate::_reg_idx!($idx), + Box::new(num), + )) + }}; + + (swp $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) { + panic!("Swap operation must be performed between registers of the same type"); + } + Instr::Move($crate::_reg_sfx!(MoveOp, Swp, $reg1)( + $crate::_reg_ty!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (mov $src_reg:ident[$src_idx:literal], $dst_reg:ident[$dst_idx:literal]) => {{ + if $crate::_reg_ty!(Reg, $src_reg) != $crate::_reg_ty!(Reg, $dst_reg) { + panic!("Move operation must be performed between registers of the same type"); + } + Instr::Move($crate::_reg_sfx!(MoveOp, Mov, $src_reg)( + $crate::_reg_ty!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_idx!($dst_idx), + )) + }}; + (dup $src_reg:ident[$src_idx:literal], $dst_reg:ident[$dst_idx:literal]) => {{ + if $crate::_reg_ty!(Reg, $src_reg) != $crate::_reg_ty!(Reg, $dst_reg) { + panic!("Dup operation must be performed between registers of the same type"); + } + Instr::Move($crate::_reg_sfx!(MoveOp, Dup, $src_reg)( + $crate::_reg_ty!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_idx!($dst_idx), + )) + }}; + (cpy $src_reg:ident[$src_idx:literal], $dst_reg:ident[$dst_idx:literal]) => {{ + if $crate::_reg_ty!(Reg, $src_reg) != $crate::_reg_ty!(Reg, $dst_reg) { + panic!("Copy operation must be performed between registers of the same type"); + } + Instr::Move($crate::_reg_sfx!(MoveOp, Cpy, $src_reg)( + $crate::_reg_ty!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_ty!(Reg, $dst_reg), + $crate::_reg_idx!($dst_idx), + )) + }}; + (cnv $src_reg:ident[$src_idx:literal], $dst_reg:ident[$dst_idx:literal]) => {{ + match ($crate::_reg_block!($src_reg), $crate::_reg_block!($dst_reg)) { + (RegBlockAFR::A, RegBlockAFR::F) => Instr::Move(MoveOp::CnvAF( + $crate::_reg_tya!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_tyf!(Reg, $dst_reg), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::F, RegBlockAFR::A) => Instr::Move(MoveOp::CnvFA( + $crate::_reg_tyf!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_tya!(Reg, $dst_reg), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::A, RegBlockAFR::A) => Instr::Move(MoveOp::CnvA( + $crate::_reg_tya!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_tya!(Reg, $src_reg), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::F, RegBlockAFR::F) => Instr::Move(MoveOp::CnvF( + $crate::_reg_tyf!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_tyf!(Reg, $src_reg), + $crate::_reg_idx!($dst_idx), + )), + (_, _) => panic!("Conversion operation between unsupported register types"), + } + }}; + (spy $src_reg:ident[$src_idx:literal], $dst_reg:ident[$dst_idx:literal]) => {{ + match ($crate::_reg_block!($src_reg), $crate::_reg_block!($dst_reg)) { + (RegBlockAFR::A, RegBlockAFR::R) => Instr::Move(MoveOp::SpyAR( + $crate::_reg_tya!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + $crate::_reg_tyr!(Reg, $dst_reg), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::R, RegBlockAFR::A) => Instr::Move(MoveOp::SpyAR( + $crate::_reg_tya!(Reg, $dst_reg), + $crate::_reg_idx!($dst_idx), + $crate::_reg_tyr!(Reg, $src_reg), + $crate::_reg_idx!($src_idx), + )), + (_, _) => { + panic!("Swap-conversion operation is supported only between A and R registers") + } + } + }}; + + (gt $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`gt` operation may be applied only to the registers of the same family"); + } + if $crate::_reg_block!($reg1) != RegBlockAFR::R { + panic!("`gt` operation for arithmetic registers requires suffix"); + } + Instr::Cmp(CmpOp::GtR( + $crate::_reg_tyr!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (gt.u $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`gt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::GtA( + SignFlag::Unsigned, + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (gt.s $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`gt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::GtA( + SignFlag::Signed, + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (gt.e $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`gt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::GtF( + FloatEqFlag::Exact, + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (gt.r $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`gt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::GtF( + FloatEqFlag::Rounding, + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (lt $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`lt` operation may be applied only to the registers of the same family"); + } + if $crate::_reg_block!($reg1) != RegBlockAFR::R { + panic!("`lt` operation for arithmetic registers requires suffix"); + } + Instr::Cmp(CmpOp::LtR( + $crate::_reg_tyr!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (lt.u $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`lt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::LtA( + SignFlag::Unsigned, + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (lt.s $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`lt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::LtA( + SignFlag::Signed, + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (lt.e $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`lt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::LtF( + FloatEqFlag::Exact, + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (lt.r $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!("`lt` operation may be applied only to the registers of the same family"); + } + Instr::Cmp(CmpOp::LtF( + FloatEqFlag::Rounding, + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (eq.e $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!( + "Equivalence check must be performed between registers of the same type and size" + ); + } + match $crate::_reg_block!($reg1) { + RegBlockAFR::A => Instr::Cmp(CmpOp::EqA( + NoneEqFlag::Equal, + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )), + RegBlockAFR::R => Instr::Cmp(CmpOp::EqR( + NoneEqFlag::Equal, + $crate::_reg_tyr!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )), + RegBlockAFR::F => Instr::Cmp(CmpOp::EqF( + FloatEqFlag::Exact, + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )), + } + }}; + (eq.n $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!( + "Equivalence check must be performed between registers of the same type and size" + ); + } + match $crate::_reg_block!($reg1) { + RegBlockAFR::A => Instr::Cmp(CmpOp::EqA( + NoneEqFlag::NonEqual, + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )), + RegBlockAFR::R => Instr::Cmp(CmpOp::EqR( + NoneEqFlag::NonEqual, + $crate::_reg_tyr!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )), + _ => panic!("Wrong registers for `eq` operation"), + } + }}; + (eq.r $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + if $crate::_reg_block!($reg1) != $crate::_reg_block!($reg2) { + panic!( + "Equivalence check must be performed between registers of the same type and size" + ); + } + Instr::Cmp(CmpOp::EqF( + FloatEqFlag::Rounding, + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($idx2), + )) + }}; + (eq s16[$idx1:literal],s16[$idx2:literal]) => {{ + Instr::Bytes(BytesOp::Eq(RegS::from($idx1), RegS::from($idx2))) + }}; + (ifn $reg:ident[$idx:literal]) => { + match $crate::_reg_block!($reg) { + RegBlockAFR::A => { + Instr::Cmp(CmpOp::IfNA($crate::_reg_tya!(Reg, $reg), $crate::_reg_idx!($idx))) + } + RegBlockAFR::R => { + Instr::Cmp(CmpOp::IfNR($crate::_reg_tyr!(Reg, $reg), $crate::_reg_idx!($idx))) + } + _ => panic!("Wrong registers for `ifn` operation"), + } + }; + (ifz $reg:ident[$idx:literal]) => { + match $crate::_reg_block!($reg) { + RegBlockAFR::A => { + Instr::Cmp(CmpOp::IfZA($crate::_reg_tya!(Reg, $reg), $crate::_reg_idx!($idx))) + } + RegBlockAFR::R => { + Instr::Cmp(CmpOp::IfZR($crate::_reg_tyr!(Reg, $reg), $crate::_reg_idx!($idx))) + } + _ => panic!("Wrong registers for `ifz` operation"), + } + }; + (st. $flag:ident $reg:ident[$idx:literal]) => { + Instr::Cmp(CmpOp::St( + $crate::_merge_flag!($flag), + $crate::_reg_tya!(Reg, $reg), + $crate::_reg_idx8!($idx), + )) + }; + (inv st0) => { + Instr::Cmp(CmpOp::StInv) + }; + + (add. $flag:ident $reg1:ident[$idx1:literal], $dst_reg:ident[$dst_idx:literal]) => { + match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { + (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::AddA( + $crate::_int_flags!($flag), + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::AddF( + $crate::_rounding_flag!($flag), + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (a, b) if a == b => panic!("addition requires integer or float registers"), + (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { + panic!("addition must be performed between registers of the same size") + } + (_, _) => panic!("addition must be performed between registers of the same type"), + } + }; + (sub. $flag:ident $reg1:ident[$idx1:literal], $dst_reg:ident[$dst_idx:literal]) => { + match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { + (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::SubA( + $crate::_int_flags!($flag), + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::SubF( + $crate::_rounding_flag!($flag), + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (a, b) if a == b => panic!("subtraction requires integer or float registers"), + (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { + panic!("subtraction must be performed between registers of the same size") + } + (_, _) => panic!("subtraction must be performed between registers of the same type"), + } + }; + (mul. $flag:ident $reg1:ident[$idx1:literal], $dst_reg:ident[$dst_idx:literal]) => { + match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { + (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::MulA( + $crate::_int_flags!($flag), + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::MulF( + $crate::_rounding_flag!($flag), + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (a, b) if a == b => panic!("multiplication requires integer or float registers"), + (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { + panic!("multiplication must be performed between registers of the same size") + } + (_, _) => panic!("multiplication must be performed between registers of the same type"), + } + }; + (div. $flag:ident $reg1:ident[$idx1:literal], $dst_reg:ident[$dst_idx:literal]) => { + match ($crate::_reg_block!($reg1), $crate::_reg_block!($dst_reg)) { + (RegBlockAFR::A, RegBlockAFR::A) => Instr::Arithmetic(ArithmeticOp::DivA( + $crate::_int_flags!($flag), + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (RegBlockAFR::F, RegBlockAFR::F) => Instr::Arithmetic(ArithmeticOp::DivF( + $crate::_rounding_flag!($flag), + $crate::_reg_tyf!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_idx!($dst_idx), + )), + (a, b) if a == b => panic!("division requires integer or float registers"), + (_, _) if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $dst_reg) => { + panic!("division must be performed between registers of the same size") + } + (_, _) => panic!("division must be performed between registers of the same type"), + } + }; + (rem $reg1:ident[$idx1:literal], $dst_reg:ident[$dst_idx:literal]) => { + if $crate::_reg_block!($reg1) != RegBlockAFR::A + || $crate::_reg_block!($dst_reg) != RegBlockAFR::A + { + panic!("modulo division must be performed only using integer arithmetic registers"); + } else { + Instr::Arithmetic(ArithmeticOp::Rem( + $crate::_reg_tya!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_tya!(Reg, $dst_reg), + $crate::_reg_idx!($dst_idx), + )) + } + }; + (inc $reg:ident[$idx:literal]) => { + Instr::Arithmetic(ArithmeticOp::Stp( + $crate::_reg_tya!(Reg, $reg), + $crate::_reg_idx!($idx), + Step::with(1), + )) + }; + (add $reg:ident[$idx:literal], $step:literal) => { + Instr::Arithmetic(ArithmeticOp::Stp( + $crate::_reg_tya!(Reg, $reg), + $crate::_reg_idx!($idx), + Step::with($step), + )) + }; + (dec $reg:ident[$idx:literal]) => { + Instr::Arithmetic(ArithmeticOp::Stp( + $crate::_reg_tya!(Reg, $reg), + $crate::_reg_idx!($idx), + Step::with(-1), + )) + }; + (sub $reg:ident[$idx:literal], $step:literal) => { + Instr::Arithmetic(ArithmeticOp::Stp( + $crate::_reg_tya!(Reg, $reg), + $crate::_reg_idx!($idx), + Step::with($step * -1), + )) + }; + (neg $reg:ident[$idx:literal]) => { + Instr::Arithmetic(ArithmeticOp::Neg( + $crate::_reg_ty!(Reg, $reg).into(), + $crate::_reg_idx16!($idx), + )) + }; + (abs $reg:ident[$idx:literal]) => { + Instr::Arithmetic(ArithmeticOp::Abs( + $crate::_reg_ty!(Reg, $reg).into(), + $crate::_reg_idx16!($idx), + )) + }; + + ( + and + $reg1:ident[$idx1:literal], + $reg2:ident[$idx2:literal], + $dst_reg:ident[$dst_idx:literal] + ) => { + if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) + || $crate::_reg_ty!(Reg, $reg2) != $crate::_reg_ty!(Reg, $dst_reg) + { + panic!("`and` operation must use the same type of registers for all of its operands"); + } else if $crate::_reg_block!($reg1) != RegBlockAFR::A + && $crate::_reg_block!($reg1) != RegBlockAFR::R + { + panic!("`and` operation requires integer arithmetic or generic registers"); + } else { + Instr::Bitwise(BitwiseOp::And( + $crate::_reg_ty!(Reg, $reg1).into(), + $crate::_reg_idx16!($idx1), + $crate::_reg_idx16!($idx2), + $crate::_reg_idx16!($dst_idx), + )) + } + }; + ( + or $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal], $dst_reg:ident[$dst_idx:literal] + ) => { + if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) + || $crate::_reg_ty!(Reg, $reg2) != $crate::_reg_ty!(Reg, $dst_reg) + { + panic!("`or` operation must use the same type of registers for all of its operands"); + } else if $crate::_reg_block!($reg1) != RegBlockAFR::A + && $crate::_reg_block!($reg1) != RegBlockAFR::R + { + panic!("`or` operation requires integer arithmetic or generic registers"); + } else { + Instr::Bitwise(BitwiseOp::Or( + $crate::_reg_ty!(Reg, $reg1).into(), + $crate::_reg_idx16!($idx1), + $crate::_reg_idx16!($idx2), + $crate::_reg_idx16!($dst_idx), + )) + } + }; + ( + xor + $reg1:ident[$idx1:literal], + $reg2:ident[$idx2:literal], + $dst_reg:ident[$dst_idx:literal] + ) => { + if $crate::_reg_ty!(Reg, $reg1) != $crate::_reg_ty!(Reg, $reg2) + || $crate::_reg_ty!(Reg, $reg2) != $crate::_reg_ty!(Reg, $dst_reg) + { + panic!("`xor` operation must use the same type of registers for all of its operands"); + } else if $crate::_reg_block!($reg1) != RegBlockAFR::A + && $crate::_reg_block!($reg1) != RegBlockAFR::R + { + panic!("`xor` operation requires integer arithmetic or generic registers"); + } else { + Instr::Bitwise(BitwiseOp::Xor( + $crate::_reg_ty!(Reg, $reg1).into(), + $crate::_reg_idx16!($idx1), + $crate::_reg_idx16!($idx2), + $crate::_reg_idx16!($dst_idx), + )) + } + }; + (shl $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + Instr::Bitwise(BitwiseOp::Shl( + $crate::_reg_tya2!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_ty!(Reg, $reg2).into(), + $crate::_reg_idx!($idx2), + )) + }; + (shr.u $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + Instr::Bitwise(BitwiseOp::ShrA( + SignFlag::Unsigned, + $crate::_reg_tya2!(Reg, $reg1), + $crate::_reg_idx16!($idx1), + $crate::_reg_ty!(Reg, $reg2), + $crate::_reg_idx!($idx2), + )) + }; + (shr.s $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + Instr::Bitwise(BitwiseOp::ShrA( + SignFlag::Signed, + $crate::_reg_tya2!(Reg, $reg1), + $crate::_reg_idx16!($idx1), + $crate::_reg_ty!(Reg, $reg2), + $crate::_reg_idx!($idx2), + )) + }; + (shr $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => {{ + Instr::Bitwise(BitwiseOp::ShrR( + $crate::_reg_tya2!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_ty!(Reg, $reg2), + $crate::_reg_idx!($idx2), + )) + }}; + (scl $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + Instr::Bitwise(BitwiseOp::Scl( + $crate::_reg_tya2!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_ty!(Reg, $reg2).into(), + $crate::_reg_idx!($idx2), + )) + }; + (scr $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + Instr::Bitwise(BitwiseOp::Scr( + $crate::_reg_tya2!(Reg, $reg1), + $crate::_reg_idx!($idx1), + $crate::_reg_ty!(Reg, $reg2).into(), + $crate::_reg_idx!($idx2), + )) + }; + (rev $reg:ident[$idx:literal]) => { + match $crate::_reg_block!($reg) { + RegBlockAFR::A => Instr::Bitwise(BitwiseOp::RevA( + $crate::_reg_tya!(Reg, $reg), + $crate::_reg_idx!($idx), + )), + RegBlockAFR::R => Instr::Bitwise(BitwiseOp::RevR( + $crate::_reg_tyr!(Reg, $reg), + $crate::_reg_idx!($idx), + )), + _ => panic!("Wrong registers for `rev` operation"), + } + }; + + (ripemd s16[$idx1:literal],r160[$idx2:literal]) => { + Instr::Digest(DigestOp::Ripemd(RegS::from($idx1), $crate::_reg_idx16!($idx2))) + }; + (sha2 s16[$idx1:literal],r256[$idx2:literal]) => { + Instr::Digest(DigestOp::Sha256(RegS::from($idx1), $crate::_reg_idx16!($idx2))) + }; + (sha2 s16[$idx1:literal],r512[$idx2:literal]) => { + Instr::Digest(DigestOp::Sha512(RegS::from($idx1), $crate::_reg_idx16!($idx2))) + }; + + (secpgen $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + if $crate::_reg_block!($reg1) != RegBlockAFR::R + || $crate::_reg_block!($reg2) != RegBlockAFR::R + { + panic!("elliptic curve instruction accept only generic registers (R-registers)"); + } else { + Instr::Secp256k1(Secp256k1Op::Gen($crate::_reg_idx!($idx1), $crate::_reg_idx8!($idx2))) + } + }; + ( + secpmul + $scalar_reg:ident[$scalar_idx:literal], + $src_reg:ident[$src_idx:literal], + $dst_reg:ident[$dst_idx:literal] + ) => { + if $crate::_reg_ty!(Reg, $src_reg) != $crate::_reg_ty!(Reg, $dst_reg) { + panic!("ecmul instruction can be used only with registers of the same type"); + } else { + Instr::Secp256k1(Secp256k1Op::Mul( + $crate::_reg_block_ar!($scalar_reg), + $crate::_reg_idx!($scalar_idx), + $crate::_reg_idx!($src_idx), + $crate::_reg_idx!($dst_idx), + )) + } + }; + (secpadd $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + if $crate::_reg_block!($reg1) != RegBlockAFR::R + || $crate::_reg_block!($reg2) != RegBlockAFR::R + { + panic!("elliptic curve instruction accept only generic registers (R-registers)"); + } else { + Instr::Secp256k1(Secp256k1Op::Add($crate::_reg_idx!($idx1), $crate::_reg_idx8!($idx2))) + } + }; + (secpneg $reg1:ident[$idx1:literal], $reg2:ident[$idx2:literal]) => { + if $crate::_reg_block!($reg1) != RegBlockAFR::R + || $crate::_reg_block!($reg2) != RegBlockAFR::R + { + panic!("elliptic curve instruction accept only generic registers (R-registers)"); + } else { + Instr::Secp256k1(Secp256k1Op::Neg($crate::_reg_idx!($idx1), $crate::_reg_idx8!($idx2))) + } + }; + { $($tt:tt)+ } => { + Instr::ExtensionCodes(isa_instr! { $( $tt )+ }) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_block_ar { + (a8) => { + RegBlockAR::A + }; + (a16) => { + RegBlockAR::A + }; + (a32) => { + RegBlockAR::A + }; + (a64) => { + RegBlockAR::A + }; + (a128) => { + RegBlockAR::A + }; + (a256) => { + RegBlockAR::A + }; + (a512) => { + RegBlockAR::A + }; + (a1024) => { + RegBlockAFR::A + }; + + (r128) => { + RegBlockAR::R + }; + (r160) => { + RegBlockAR::R + }; + (r256) => { + RegBlockAR::R + }; + (r512) => { + RegBlockAR::R + }; + (r1024) => { + RegBlockAR::R + }; + (r2048) => { + RegBlockAR::R + }; + (r4096) => { + RegBlockAR::R + }; + (r8192) => { + RegBlockAR::R + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_block { + (a8) => { + RegBlockAFR::A + }; + (a16) => { + RegBlockAFR::A + }; + (a32) => { + RegBlockAFR::A + }; + (a64) => { + RegBlockAFR::A + }; + (a128) => { + RegBlockAFR::A + }; + (a256) => { + RegBlockAFR::A + }; + (a512) => { + RegBlockAFR::A + }; + (a1024) => { + RegBlockAFR::A + }; + + (f16b) => { + RegBlockAFR::F + }; + (f16) => { + RegBlockAFR::F + }; + (f32) => { + RegBlockAFR::F + }; + (f64) => { + RegBlockAFR::F + }; + (f80) => { + RegBlockAFR::F + }; + (f128) => { + RegBlockAFR::F + }; + (f256) => { + RegBlockAFR::F + }; + (f512) => { + RegBlockAFR::F + }; + + (r128) => { + RegBlockAFR::R + }; + (r160) => { + RegBlockAFR::R + }; + (r256) => { + RegBlockAFR::R + }; + (r512) => { + RegBlockAFR::R + }; + (r1024) => { + RegBlockAFR::R + }; + (r2048) => { + RegBlockAFR::R + }; + (r4096) => { + RegBlockAFR::R + }; + (r8192) => { + RegBlockAFR::R + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_sfx { + ($a:ident, $b:ident,a8) => { + $crate::paste! { $a :: [<$b A>] } + }; + ($a:ident, $b:ident,a16) => { + $crate::paste! { $a :: [<$b A>] } + }; + ($a:ident, $b:ident,a32) => { + $crate::paste! { $a :: [<$b A>] } + }; + ($a:ident, $b:ident,a64) => { + $crate::paste! { $a :: [<$b A>] } + }; + ($a:ident, $b:ident,a128) => { + $crate::paste! { $a :: [<$b A>] } + }; + ($a:ident, $b:ident,a256) => { + $crate::paste! { $a :: [<$b A>] } + }; + ($a:ident, $b:ident,a512) => { + $crate::paste! { $a :: [<$b A>] } + }; + ($a:ident, $b:ident,a1024) => { + $crate::paste! { $a :: [<$b A>] } + }; + + ($a:ident, $b:ident,f16b) => { + $crate::paste! { $a :: [<$b F>] } + }; + ($a:ident, $b:ident,f16) => { + $crate::paste! { $a :: [<$b F>] } + }; + ($a:ident, $b:ident,f32) => { + $crate::paste! { $a :: [<$b F>] } + }; + ($a:ident, $b:ident,f64) => { + $crate::paste! { $a :: [<$b F>] } + }; + ($a:ident, $b:ident,f80) => { + $crate::paste! { $a :: [<$b F>] } + }; + ($a:ident, $b:ident,f128) => { + $crate::paste! { $a :: [<$b F>] } + }; + ($a:ident, $b:ident,f256) => { + $crate::paste! { $a :: [<$b F>] } + }; + ($a:ident, $b:ident,f512) => { + $crate::paste! { $a :: [<$b F>] } + }; + + ($a:ident, $b:ident,r128) => { + $crate::paste! { $a :: [<$b R>] } + }; + ($a:ident, $b:ident,r160) => { + $crate::paste! { $a :: [<$b R>] } + }; + ($a:ident, $b:ident,r256) => { + $crate::paste! { $a :: [<$b R>] } + }; + ($a:ident, $b:ident,r512) => { + $crate::paste! { $a :: [<$b R>] } + }; + ($a:ident, $b:ident,r1024) => { + $crate::paste! { $a :: [<$b R>] } + }; + ($a:ident, $b:ident,r2048) => { + $crate::paste! { $a :: [<$b R>] } + }; + ($a:ident, $b:ident,r4096) => { + $crate::paste! { $a :: [<$b R>] } + }; + ($a:ident, $b:ident,r8192) => { + $crate::paste! { $a :: [<$b R>] } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_ty { + ($ident:ident,a8) => { + $crate::paste! { [<$ident A>] :: A8 } + }; + ($ident:ident,a16) => { + $crate::paste! { [<$ident A>] :: A16 } + }; + ($ident:ident,a32) => { + $crate::paste! { [<$ident A>] :: A32 } + }; + ($ident:ident,a64) => { + $crate::paste! { [<$ident A>] :: A64 } + }; + ($ident:ident,a128) => { + $crate::paste! { [<$ident A>] :: A128 } + }; + ($ident:ident,a256) => { + $crate::paste! { [<$ident A>] :: A256 } + }; + ($ident:ident,a512) => { + $crate::paste! { [<$ident A>] :: A512 } + }; + ($ident:ident,a1024) => { + $crate::paste! { [<$ident A>] :: A1024 } + }; + + ($ident:ident,f16b) => { + $crate::paste! { [<$ident F>] :: F16B } + }; + ($ident:ident,f16) => { + $crate::paste! { [<$ident F>] :: F16 } + }; + ($ident:ident,f32) => { + $crate::paste! { [<$ident F>] :: F32 } + }; + ($ident:ident,f64) => { + $crate::paste! { [<$ident F>] :: F64 } + }; + ($ident:ident,f80) => { + $crate::paste! { [<$ident F>] :: F80 } + }; + ($ident:ident,f128) => { + $crate::paste! { [<$ident F>] :: F128 } + }; + ($ident:ident,f256) => { + $crate::paste! { [<$ident F>] :: F256 } + }; + ($ident:ident,f512) => { + $crate::paste! { [<$ident F>] :: F512 } + }; + + ($ident:ident,r128) => { + $crate::paste! { [<$ident R>] :: R128 } + }; + ($ident:ident,r160) => { + $crate::paste! { [<$ident R>] :: R160 } + }; + ($ident:ident,r256) => { + $crate::paste! { [<$ident R>] :: R256 } + }; + ($ident:ident,r512) => { + $crate::paste! { [<$ident R>] :: R512 } + }; + ($ident:ident,r1024) => { + $crate::paste! { [<$ident R>] :: R1024 } + }; + ($ident:ident,r2048) => { + $crate::paste! { [<$ident R>] :: R2048 } + }; + ($ident:ident,r4096) => { + $crate::paste! { [<$ident R>] :: R4096 } + }; + ($ident:ident,r8192) => { + $crate::paste! { [<$ident R>] :: R8192 } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_tya2 { + ($ident:ident,a8) => { + $crate::paste! { [<$ident A2>] :: A8 } + }; + ($ident:ident,a16) => { + $crate::paste! { [<$ident A2>] :: A16 } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_tya { + ($ident:ident,a8) => { + $crate::paste! { [<$ident A>] :: A8 } + }; + ($ident:ident,a16) => { + $crate::paste! { [<$ident A>] :: A16 } + }; + ($ident:ident,a32) => { + $crate::paste! { [<$ident A>] :: A32 } + }; + ($ident:ident,a64) => { + $crate::paste! { [<$ident A>] :: A64 } + }; + ($ident:ident,a128) => { + $crate::paste! { [<$ident A>] :: A128 } + }; + ($ident:ident,a256) => { + $crate::paste! { [<$ident A>] :: A256 } + }; + ($ident:ident,a512) => { + $crate::paste! { [<$ident A>] :: A512 } + }; + ($ident:ident,a1024) => { + $crate::paste! { [<$ident A>] :: A1024 } + }; + ($ident:ident, $other:ident) => { + panic!("operation requires `A` register") + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_tyf { + ($ident:ident,f16b) => { + $crate::paste! { [<$ident F>] :: F16B } + }; + ($ident:ident,f16) => { + $crate::paste! { [<$ident F>] :: F16 } + }; + ($ident:ident,f32) => { + $crate::paste! { [<$ident F>] :: F32 } + }; + ($ident:ident,f64) => { + $crate::paste! { [<$ident F>] :: F64 } + }; + ($ident:ident,f80) => { + $crate::paste! { [<$ident F>] :: F80 } + }; + ($ident:ident,f128) => { + $crate::paste! { [<$ident F>] :: F128 } + }; + ($ident:ident,f256) => { + $crate::paste! { [<$ident F>] :: F256 } + }; + ($ident:ident,f512) => { + $crate::paste! { [<$ident F>] :: F512 } + }; + ($ident:ident, $other:ident) => { + panic!("operation requires `F` register") + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_tyr { + ($ident:ident,r128) => { + $crate::paste! { [<$ident R>] :: R128 } + }; + ($ident:ident,r160) => { + $crate::paste! { [<$ident R>] :: R160 } + }; + ($ident:ident,r256) => { + $crate::paste! { [<$ident R>] :: R256 } + }; + ($ident:ident,r512) => { + $crate::paste! { [<$ident R>] :: R512 } + }; + ($ident:ident,r1024) => { + $crate::paste! { [<$ident R>] :: R1024 } + }; + ($ident:ident,r2048) => { + $crate::paste! { [<$ident R>] :: R2048 } + }; + ($ident:ident,r4096) => { + $crate::paste! { [<$ident R>] :: R4096 } + }; + ($ident:ident,r8192) => { + $crate::paste! { [<$ident R>] :: R8192 } + }; + ($ident:ident, $other:ident) => { + panic!("operation requires `R` register") + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_idx { + ($idx:literal) => { + $crate::paste! { Reg32::[] } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_idx8 { + ($idx:literal) => { + $crate::paste! { Reg8::[] } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _reg_idx16 { + ($idx:literal) => { + $crate::paste! { Reg16::[] } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _merge_flag { + (s) => { + MergeFlag::Set + }; + (a) => { + MergeFlag::Add + }; + (n) => { + MergeFlag::And + }; + (o) => { + MergeFlag::Or + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _rounding_flag { + (z) => { + RoundingFlag::TowardsZero + }; + (n) => { + RoundingFlag::TowardsNearest + }; + (f) => { + RoundingFlag::Floor + }; + (c) => { + RoundingFlag::Ceil + }; + ($other:ident) => { + panic!("wrong float rounding flag") + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _int_flags { + (uc) => { + IntFlags::unsigned_checked() + }; + (cu) => { + IntFlags::unsigned_checked() + }; + (sc) => { + IntFlags::signed_checked() + }; + (cs) => { + IntFlags::signed_checked() + }; + (uw) => { + IntFlags::unsigned_wrapped() + }; + (wu) => { + IntFlags::unsigned_wrapped() + }; + (sw) => { + IntFlags::signed_wrapped() + }; + (ws) => { + IntFlags::signed_wrapped() + }; + ($other:ident) => { + panic!("wrong integer operation flags") + }; +} diff --git a/src/isa/mod.rs b/src/isa/mod.rs index fe9be49..9cd568d 100644 --- a/src/isa/mod.rs +++ b/src/isa/mod.rs @@ -23,6 +23,8 @@ //! AluVM instruction set architecture +#[macro_use] +mod macros; mod bytecode; mod exec; mod flags; diff --git a/src/library/cursor.rs b/src/library/cursor.rs index e5bdaa1..8eeb99b 100644 --- a/src/library/cursor.rs +++ b/src/library/cursor.rs @@ -119,6 +119,9 @@ where Cursor { bytecode, byte_pos: 0, bit_pos: u3::MIN, data, libs } } + /// Returns the current offset of the cursor + pub const fn offset(&self) -> (u16, u3) { (self.byte_pos, self.bit_pos) } + /// Converts writer into data segment #[inline] pub fn into_data_segment(self) -> D { self.data } @@ -435,8 +438,9 @@ where ) -> Result<(), WriteError> { let len = reg.bytes(); assert!( - len <= value.len(), - "value for the register has larger bit length than the register" + len >= value.len(), + "value for the register has larger bit length {} than the register {len}", + value.len() ); value.reshape(reg.layout().using_sign(value.layout())); let offset = self.write_unique(&value[..])?; @@ -464,8 +468,9 @@ mod tests { use amplify::num::{u2, u3, u5, u7}; use super::Cursor; - use crate::data::ByteStr; + use crate::data::{ByteStr, Number}; use crate::library::{LibSeg, Read, Write}; + use crate::reg::RegA; #[test] fn read() { @@ -502,7 +507,7 @@ mod tests { #[test] fn write() { let libseg = LibSeg::default(); - let mut code = [0, 0, 0, 0, 0, 0]; + let mut code = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let mut cursor = Cursor::<_, ByteStr>::new(&mut code, &libseg); cursor.write_u2(u2::with(0b00000011)).unwrap(); cursor.write_u3(u3::with(0b00000101)).unwrap(); @@ -512,15 +517,28 @@ mod tests { cursor.write_u3(u3::with(0b00000110)).unwrap(); let two_bytes = 0b11110000_10101010u16; cursor.write_u16(two_bytes).unwrap(); + let number = Number::from(255u8); + cursor.write_number(RegA::A8, number).unwrap(); - let mut cursor = Cursor::<_, ByteStr>::new(code, &libseg); + let data = cursor.data; + let mut cursor = Cursor::<_, ByteStr>::with(code, data, &libseg); assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000101); assert_eq!(cursor.read_u7().unwrap().to_u8(), 0b01011111); assert_eq!(cursor.read_u8().unwrap(), 0b11100111); - assert_eq!(cursor.read_bool().unwrap(), true); + assert!(cursor.read_bool().unwrap()); assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000110); assert_eq!(cursor.read_u16().unwrap(), two_bytes); + assert_eq!(cursor.read_number(RegA::A8).unwrap(), number); + } + + #[test] + #[should_panic] + fn write_fail() { + let libseg = LibSeg::default(); + let mut code = [0, 0, 0, 0, 0, 0]; + let mut cursor = Cursor::<_, ByteStr>::new(&mut code, &libseg); + cursor.write_number(RegA::A8, Number::from(256u16)).unwrap(); } #[test] diff --git a/src/library/lib.rs b/src/library/lib.rs index 4aaaaa4..46b9ce1 100644 --- a/src/library/lib.rs +++ b/src/library/lib.rs @@ -28,6 +28,7 @@ use core::convert::TryFrom; use core::fmt::{self, Display, Formatter}; use core::hash::{Hash as RustHash, Hasher}; use core::str::FromStr; +use std::io; use amplify::{ByteArray, Bytes32}; use baid58::{Baid58ParseError, FromBaid58, ToBaid58}; @@ -35,7 +36,7 @@ use sha2::{Digest, Sha256}; use super::{Cursor, Read}; use crate::data::ByteStr; -use crate::isa::{BytecodeError, ExecStep, InstructionSet}; +use crate::isa::{Bytecode, BytecodeError, ExecStep, Instr, InstructionSet}; use crate::library::segs::IsaSeg; use crate::library::{CodeEofError, LibSeg, LibSegOverflow, SegmentError}; use crate::reg::CoreRegs; @@ -136,7 +137,11 @@ impl Display for Lib { writeln!(f, "ISAE: {}", &self.isae)?; write!(f, "CODE:\n{:#10}", self.code)?; write!(f, "DATA:\n{:#10}", self.data)?; - write!(f, "LIBS: {:8}", self.libs) + if self.libs.count() > 0 { + write!(f, "LIBS: {:8}", self.libs) + } else { + write!(f, "LIBS: none") + } } } @@ -243,6 +248,23 @@ impl Lib { Ok(code) } + /// Disassembles library into a set of instructions and offsets and prints it to the writer. + pub fn print_disassemble(&self, mut writer: impl io::Write) -> Result<(), io::Error> + where + Isa: InstructionSet, + { + let mut reader = Cursor::with(&self.code, &self.data, &self.libs); + while !reader.is_eof() { + let pos = reader.offset().0 as usize; + write!(writer, "offset_0x{pos:04X}: ")?; + match Instr::::decode(&mut reader) { + Ok(instr) => writeln!(writer, "{instr}")?, + Err(_) => writeln!(writer, "\n{}", ByteStr::with(&self.code.as_ref()[pos..]))?, + } + } + Ok(()) + } + /// Returns hash identifier [`LibId`], representing the library in a unique way. /// /// Lib ID is computed as SHA256 tagged hash of the serialized library segments (ISAE, code, @@ -282,28 +304,64 @@ impl Lib { where Isa: InstructionSet, { - let mut cursor = Cursor::with(&self.code.bytes[..], &self.data, &self.libs); + #[cfg(all(debug_assertions, feature = "std"))] + let (m, w, d, g, r, y, z) = ( + "\x1B[0;35m", + "\x1B[1;1m", + "\x1B[0;37;2m", + "\x1B[0;32m", + "\x1B[0;31m", + "\x1B[0;33m", + "\x1B[0m", + ); + + let mut cursor = Cursor::with(&self.code, &self.data, &self.libs); let lib_hash = self.id(); cursor.seek(entrypoint).ok()?; + let mut st0 = registers.st0; while !cursor.is_eof() { let pos = cursor.pos(); let instr = Isa::decode(&mut cursor).ok()?; + + #[cfg(all(debug_assertions, feature = "std"))] + { + eprint!("{m}@{pos:06}:{z} {: <32}; ", instr.to_string()); + for reg in instr.src_regs() { + let val = registers.get(reg); + eprint!("{d}{reg}={z}{w}{val}{z} "); + } + } + let next = instr.exec(registers, LibSite::with(pos, lib_hash), context); #[cfg(all(debug_assertions, feature = "std"))] - eprint!("@{:06}> {}; st0={}", pos, instr, registers.st0); + { + eprint!("-> "); + for reg in instr.dst_regs() { + let val = registers.get(reg); + eprint!("{g}{reg}={y}{val}{z} "); + } + if st0 != registers.st0 { + let c = if registers.st0 { g } else { r }; + eprint!(" {d}st0={z}{c}{}{z} ", registers.st0); + } + } + st0 = registers.st0; if !registers.acc_complexity(instr) { #[cfg(all(debug_assertions, feature = "std"))] - eprintln!(" -> complexity overflow"); + eprintln!("complexity overflow"); return None; } match next { ExecStep::Stop => { #[cfg(all(debug_assertions, feature = "std"))] - eprintln!(" -> execution stopped"); + { + let c = if registers.st0 { g } else { r }; + eprintln!("execution stopped; {d}st0={z}{c}{}{z}", registers.st0); + } return None; } ExecStep::Next => { @@ -313,12 +371,12 @@ impl Lib { } ExecStep::Jump(pos) => { #[cfg(all(debug_assertions, feature = "std"))] - eprintln!(" -> {}", pos); + eprintln!("{}", pos); cursor.seek(pos).ok()?; } ExecStep::Call(site) => { #[cfg(all(debug_assertions, feature = "std"))] - eprintln!(" -> {}", site); + eprintln!("{}", site); return Some(site); } } @@ -355,7 +413,7 @@ mod test { #[test] fn lib_id_display() { - let id = LibId::with("FLOAT", &b"", &b"", &none!()); + let id = LibId::with("FLOAT", b"", b"", &none!()); assert_eq!( format!("{id}"), "urn:ubideco:alu:GrjjwmeTsibiEeYYtjokmc8j4Jn1KWL2SX8NugG6T5kZ#pinball-eternal-colombo" @@ -368,7 +426,7 @@ mod test { #[test] fn lib_id_from_str() { - let id = LibId::with("FLOAT", &b"", &b"", &none!()); + let id = LibId::with("FLOAT", b"", b"", &none!()); assert_eq!( Ok(id), LibId::from_str( diff --git a/src/reg/core_regs.rs b/src/reg/core_regs.rs index 0c1c6ac..ad393ee 100644 --- a/src/reg/core_regs.rs +++ b/src/reg/core_regs.rs @@ -31,8 +31,8 @@ use amplify::num::apfloat::{ieee, Float}; use amplify::num::{u1024, u256, u512}; use half::bf16; -use super::{Reg32, RegA, RegAFR, RegF, RegR, RegS}; -use crate::data::{ByteStr, MaybeNumber, Number}; +use super::{Reg, Reg32, RegA, RegAFR, RegF, RegR, RegS}; +use crate::data::{ByteStr, MaybeNumber, Number, RegValue}; use crate::isa::InstructionSet; use crate::library::LibSite; @@ -206,8 +206,26 @@ impl CoreRegs { } } - /// Retrieves register value - pub fn get(&self, reg: impl Into, index: impl Into) -> MaybeNumber { + /// Extracts value for any type of registers + pub fn get(&self, reg: impl Into) -> RegValue { + match reg.into() { + Reg::A(reg, index) => self.get_n(reg, index).into(), + Reg::F(reg, index) => self.get_n(reg, index).into(), + Reg::R(reg, index) => self.get_n(reg, index).into(), + Reg::S(reg) => self.get_s(reg).cloned().into(), + } + } + + /// Iterates over values from registers + pub fn get_list<'a>( + &'a self, + reg: impl IntoIterator> + 'a, + ) -> impl Iterator + 'a { + reg.into_iter().map(move |reg| self.get(reg)) + } + + /// Retrieves numeric register value + pub fn get_n(&self, reg: impl Into, index: impl Into) -> MaybeNumber { let index = index.into() as usize; match reg.into() { RegAFR::A(a) => { @@ -282,20 +300,20 @@ impl CoreRegs { /// Returns value from two registers only if both of them contain a value; otherwise returns /// `None`. #[inline] - pub fn get_both( + pub fn get_n2( &self, reg1: impl Into, idx1: impl Into, reg2: impl Into, idx2: impl Into, ) -> Option<(Number, Number)> { - self.get(reg1, idx1).and_then(|val1| self.get(reg2, idx2).map(|val2| (val1, val2))) + self.get_n(reg1, idx1).and_then(|val1| self.get_n(reg2, idx2).map(|val2| (val1, val2))) } /// Returns value from two string (`S`) registers only if both of them contain a value; /// otherwise returns `None`. #[inline] - pub fn get_both_s( + pub fn get_s2( &self, idx1: impl Into, idx2: impl Into, @@ -307,7 +325,7 @@ impl CoreRegs { /// until the value fits register bit size. /// /// Returns `true` if the value was not `None` - pub fn set( + pub fn set_n( &mut self, reg: impl Into, index: impl Into, @@ -355,7 +373,7 @@ impl CoreRegs { /// /// Returns `false` if the register is initialized and the value is not `None`. #[inline] - pub fn set_if( + pub fn set_n_if( &mut self, reg: impl Into, index: impl Into, @@ -363,8 +381,8 @@ impl CoreRegs { ) -> bool { let reg = reg.into(); let index = index.into(); - if self.get(reg, index).is_none() { - self.set(reg, index, value) + if self.get_n(reg, index).is_none() { + self.set_n(reg, index, value) } else { value.into().is_none() } @@ -407,11 +425,11 @@ impl CoreRegs { dst: impl Into, op: fn(Number, Number) -> Number, ) { - let reg_val = match (*self.get(reg1.into(), src1), *self.get(reg2.into(), src2)) { + let reg_val = match (*self.get_n(reg1.into(), src1), *self.get_n(reg2.into(), src2)) { (None, None) | (None, Some(_)) | (Some(_), None) => MaybeNumber::none(), (Some(val1), Some(val2)) => op(val1, val2).into(), }; - self.set(reg3.into(), dst, reg_val); + self.set_n(reg3.into(), dst, reg_val); } /// Accumulates complexity of the instruction into `ca0`. @@ -792,19 +810,19 @@ mod test { for reg in RegA::ALL { for idx in Reg32::ALL { - regs.set(reg, idx, u8::from(idx)); + regs.set_n(reg, idx, u8::from(idx)); } } for reg in RegF::ALL { for idx in Reg32::ALL { - regs.set(reg, idx, u8::from(idx)); + regs.set_n(reg, idx, u8::from(idx)); } } for reg in RegR::ALL { for idx in Reg32::ALL { - regs.set(reg, idx, u8::from(idx)); + regs.set_n(reg, idx, u8::from(idx)); } } diff --git a/src/reg/indexes.rs b/src/reg/indexes.rs index 5ab8a04..bef2e3c 100644 --- a/src/reg/indexes.rs +++ b/src/reg/indexes.rs @@ -613,6 +613,10 @@ impl From<&RegS> for u5 { fn from(reg: &RegS) -> Self { u5::with(reg.0.to_u8()) } } +impl From for Reg32 { + fn from(reg: RegS) -> Self { u5::from(reg.0).into() } +} + impl TryFrom for RegS { type Error = OverflowError; diff --git a/src/reg/mod.rs b/src/reg/mod.rs index 9e0b460..bdfb20d 100644 --- a/src/reg/mod.rs +++ b/src/reg/mod.rs @@ -39,3 +39,65 @@ pub trait Register: Default { /// Text description of the register family fn description() -> &'static str; } + +/// Superset of all registers accessible via instructions. The superset includes `A`, `F`, `R` and +/// `S` families of registers. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] +pub enum Reg { + /// Arithmetic integer registers (`A` registers) + #[display("{0}{1}")] + A(RegA, Reg32), + + /// Arithmetic float registers (`F` registers) + #[display("{0}{1}")] + F(RegF, Reg32), + + /// Non-arithmetic (general) registers (`R` registers) + #[display("{0}{1}")] + R(RegR, Reg32), + + /// String registers (`S` registers) + #[display("{0}")] + #[from] + S(RegS), +} + +impl Reg { + /// Construct register information + pub fn new(reg: impl Into, index: impl Into) -> Self { + let index = index.into(); + match reg.into() { + RegAFR::A(reg) => Reg::A(reg, index), + RegAFR::F(reg) => Reg::F(reg, index), + RegAFR::R(reg) => Reg::R(reg, index), + } + } + + /// Returns family ([`RegBlock`]) of the register + pub fn family(self) -> RegBlock { + match self { + Reg::A(_, _) => RegBlock::A, + Reg::F(_, _) => RegBlock::F, + Reg::R(_, _) => RegBlock::R, + Reg::S(_) => RegBlock::S, + } + } + + /// Returns specific register ([`RegAll`]) of the register + pub fn register(self) -> RegAll { + match self { + Reg::A(reg, _) => RegAll::A(reg), + Reg::F(reg, _) => RegAll::F(reg), + Reg::R(reg, _) => RegAll::R(reg), + Reg::S(_) => RegAll::S, + } + } + + /// Returns register index + pub fn index(self) -> Reg32 { + match self { + Reg::A(_, index) | Reg::F(_, index) | Reg::R(_, index) => index, + Reg::S(index) => index.into(), + } + } +}