diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de5813a..f3c2a58 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,12 +5,12 @@ on: branches: - master tags: - - 'v[0-9]+\.*' + - 'v[0-9]+.*' pull_request: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always @@ -35,9 +35,7 @@ jobs: matrix: feature: - log - - ascii-armor - - secp256k1 - - curve25519 + - armor - serde - stl steps: @@ -47,19 +45,6 @@ jobs: run: cargo check --workspace --no-default-features --features=std,${{matrix.feature}} - name: Feature ${{matrix.feature}} run: cargo check --workspace --features=${{matrix.feature}} - features-nostd: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - feature: - - secp256k1 - - curve25519 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - name: Feature ${{matrix.feature}} - run: cargo check --workspace --no-default-features --features=alloc,${{matrix.feature}} platforms: runs-on: ${{ matrix.os }} strategy: @@ -76,7 +61,7 @@ jobs: strategy: fail-fast: false matrix: - toolchain: [ nightly, beta, stable, 1.77.0 ] + toolchain: [ nightly, beta, stable, 1.81.0 ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 3711d7a..9ba863f 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -5,12 +5,12 @@ on: branches: - master tags: - - 'v[0-9]+\.*' + - 'v[0-9]+.*' pull_request: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fb630fb..91f36b6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,7 +5,7 @@ on: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always @@ -38,3 +38,8 @@ jobs: components: rust-docs - name: Formatting run: cargo +nightly doc --workspace --all-features + typos: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crate-ci/typos@master diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9821d00..7b00956 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,12 +5,12 @@ on: branches: - master tags: - - 'v[0-9]+\.*' + - 'v[0-9]+.*' pull_request: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always diff --git a/.rustfmt.toml b/.rustfmt.toml index 5704008..29c789a 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -5,9 +5,10 @@ max_width = 100 array_width = 100 attr_fn_like_width = 100 comment_width = 100 -chain_width = 100 +chain_width = 60 fn_call_width = 100 single_line_if_else_max_width = 100 +struct_lit_width = 60 fn_single_line = true format_code_in_doc_comments = true diff --git a/Cargo.lock b/Cargo.lock index 2e20520..6fa6374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,21 +4,16 @@ version = 3 [[package]] name = "aluvm" -version = "0.11.0-beta.9" +version = "0.12.0-beta.4" dependencies = [ "amplify", "ascii-armor", "baid64", - "blake3", - "curve25519-dalek", - "getrandom 0.2.15", - "half", + "commit_verify", + "getrandom", "paste", "rand", - "ripemd", - "secp256k1", "serde", - "sha2", "strict_encoding", "strict_types", "wasm-bindgen", @@ -31,7 +26,6 @@ version = "4.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "448cf0c3afc71439b5f837aac5399a1ef2b223f5f38324dbfb4343deec3b80cc" dependencies = [ - "amplify_apfloat", "amplify_derive", "amplify_num", "amplify_syn", @@ -41,17 +35,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "amplify_apfloat" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695e433882668b55b3d7fb0ba22bf9be66a91abe30d7ca1f1a774f8b90b4db4c" -dependencies = [ - "amplify_num", - "bitflags", - "wasm-bindgen", -] - [[package]] name = "amplify_derive" version = "4.0.1" @@ -85,18 +68,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "ascii" version = "1.1.0" @@ -108,9 +79,9 @@ dependencies = [ [[package]] name = "ascii-armor" -version = "0.7.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4966ac403dc4a666d8131dfe4df684f45acc68d4c7e768db89c463aa5617910" +checksum = "0269eb842ec952b027df0fc33184b6a0dea5ea473160b36992274eb53758461e" dependencies = [ "amplify", "baid64", @@ -121,9 +92,9 @@ dependencies = [ [[package]] name = "baid64" -version = "0.2.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95dabc2759e01e2c382968639868a701f384a18890934f9e75d4feb4d6623794" +checksum = "6cb4a8b2f1afee4ef00a190b260ad871842b93206177b59631fecd325d48d538" dependencies = [ "amplify", "base64", @@ -146,41 +117,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bitcoin-io" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" - -[[package]] -name = "bitcoin_hashes" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" -dependencies = [ - "bitcoin-io", - "hex-conservative", -] - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "blake3" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -204,9 +140,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.34" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ "shlex", ] @@ -218,36 +154,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "console_error_panic_hook" -version = "0.1.7" +name = "commit_encoding_derive" +version = "0.12.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +checksum = "9613582af45e1564c09d813dae82e7c7bca95678b9c42dae955a8499ecc6ae95" dependencies = [ - "cfg-if", - "wasm-bindgen", + "amplify", + "amplify_syn", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "constant_time_eq" -version = "0.3.1" +name = "commit_verify" +version = "0.12.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "6b822f3253ddad821c2a08b88d6534cc322e57dfe3aa8a5bbe88cce8beecc965" +dependencies = [ + "amplify", + "commit_encoding_derive", + "ripemd", + "sha2", + "strict_encoding", + "strict_types", + "vesper-lang", +] [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-common" version = "0.1.6" @@ -258,28 +200,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "curve25519-dalek" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.7" @@ -306,17 +226,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -326,25 +235,15 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] - [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -352,20 +251,11 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hex-conservative" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" -dependencies = [ - "arrayvec", -] - [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -373,18 +263,19 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.161" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "log" @@ -394,9 +285,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "minicov" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def6d99771d7c499c26ad4d40eb6645eafd3a1553b35fc26ea5a489a45e82d9a" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" dependencies = [ "cc", "walkdir", @@ -431,9 +322,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -455,7 +346,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -465,16 +356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -483,7 +365,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom", ] [[package]] @@ -492,7 +374,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -510,44 +392,24 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" -[[package]] -name = "secp256k1" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" -dependencies = [ - "bitcoin_hashes", - "rand", - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" -dependencies = [ - "cc", -] - [[package]] name = "serde" -version = "1.0.214" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.91", ] [[package]] @@ -568,7 +430,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -579,12 +441,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "strict_encoding" -version = "2.7.1" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f84deeb6b9e839d4e4f67040aca49c13d0191e57f5383dc73c2626bca248d96" +checksum = "8fd36b71bb44ca146be0b2185ed6c6deb3684cc0d5c3a94284e97fe7fa6a642f" dependencies = [ "amplify", - "half", "serde", "strict_encoding_derive", "wasm-bindgen", @@ -592,9 +453,9 @@ dependencies = [ [[package]] name = "strict_encoding_derive" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119f2044ee7438c2f25330cafd684be868de4f51621483de8ad8c442dc80bdb0" +checksum = "34e3bc6e4a2450420b4dbfb6929d9ce005e8c36cf73bf215db99f0d09c9fa79f" dependencies = [ "amplify_syn", "heck", @@ -605,14 +466,13 @@ dependencies = [ [[package]] name = "strict_types" -version = "2.7.2" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bae7475fc901144d8a35d25e36d76aa020b840f233d60532d6d52318718781b" +checksum = "3e208d1bd29a0f70d7ee90a91d76446b8bb7b1b8212d06e031e396d52e2fe891" dependencies = [ "amplify", "ascii-armor", "baid64", - "half", "indexmap", "sha2", "strict_encoding", @@ -630,12 +490,6 @@ dependencies = [ "serde_str_helpers", ] -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "1.0.109" @@ -649,9 +503,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", @@ -660,22 +514,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3c6efbfc763e64eb85c11c25320f0737cb7364c4b6336db90aa9ebe27a0bbd" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.91", ] [[package]] @@ -686,9 +540,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "version_check" @@ -698,9 +552,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vesper-lang" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72ebd3b32f16ee8ace2bd3058c2bfa0f4820992bd4ea86e73ba228bb13dd2b0" +checksum = "cd2b7e3e27aeb0524204e58042f6e4531a720745d1b1a3978d3a084f1885f63d" dependencies = [ "amplify", "strict_encoding", @@ -716,12 +570,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -730,9 +578,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -741,36 +589,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.91", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -778,30 +626,29 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-bindgen-test" -version = "0.3.45" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426" +checksum = "c61d44563646eb934577f2772656c7ad5e9c90fac78aa8013d776fcdaf24625d" dependencies = [ - "console_error_panic_hook", "js-sys", "minicov", "scoped-tls", @@ -812,20 +659,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.45" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0" +checksum = "54171416ce73aa0b9c377b51cc3cb542becee1cd678204812e8392e5b0e4a031" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.91", ] [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -931,11 +778,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.91", ] - -[[package]] -name = "zeroize" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" diff --git a/Cargo.toml b/Cargo.toml index 0160422..ec94d5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "aluvm" description = "Functional registry-based RISC virtual machine" -version = "0.11.0-beta.9" +version = "0.12.0-beta.4" authors = ["Dr Maxim Orlovsky "] repository = "https://github.com/aluvm/rust-aluvm" homepage = "https://aluvm.org" keywords = ["virtual-machine", "emulator", "functional", "risc", "edge-computing"] categories = ["no-std", "embedded", "compilers", "cryptography", "emulators"] -rust-version = "1.77.0" +rust-version = "1.81.0" # Due to `Error` in `core` edition = "2021" license = "Apache-2.0" readme = "README.md" @@ -21,29 +21,25 @@ name = "aluvm-stl" required-features = ["stl"] [dependencies] -amplify = { version = "4.8.0", default-features = false, features = ["apfloat", "derive", "hex"] } -ascii-armor = { version = "0.7.2", optional = true } -baid64 = "0.2.2" +amplify = { version = "~4.8.0", default-features = false, features = ["derive"] } +ascii-armor = { version = "0.9.0", optional = true } +baid64 = "0.4.1" +commit_verify = "0.12.0-beta.4" paste = "1" -strict_encoding = { version = "2.7.0", default-features = false, features = ["float", "derive"] } -strict_types = { version = "2.7.2", optional = true } -sha2 = "0.10.8" -blake3 = "1.5.4" -ripemd = "0.1.3" -secp256k1 = { version = "0.30.0", optional = true, features = ["global-context"] } -curve25519-dalek = { version = "3.2.1", optional = true } -half = "2.4.1" # Required to maintain MSRV -serde_crate = { package = "serde", version = "1", optional = true } +strict_encoding = { version = "~2.8.1", default-features = false, features = ["derive"] } +strict_types = { version = "~2.8.1", optional = true } +serde = { version = "1", optional = true } [features] -default = ["std"] -all = ["stl", "std", "log", "secp256k1", "curve25519", "serde", "ascii-armor"] -stl = ["strict_types/armor", "std"] -std = ["amplify/std", "alloc"] -log = ["std"] +default = [] +all = ["std", "stl", "log", "armor", "serde"] + +std = ["amplify/std"] +armor = ["dep:ascii-armor", "strict_types/armor"] +stl = ["armor", "strict_types"] +log = [] alloc = ["amplify/alloc"] -curve25519 = ["curve25519-dalek"] -serde = ["serde_crate", "amplify/serde", "std", "strict_encoding/serde"] +serde = ["dep:serde", "amplify/serde", "strict_encoding/serde"] [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" diff --git a/LICENSE b/LICENSE index fd4000d..d9a10c0 100644 --- a/LICENSE +++ b/LICENSE @@ -174,30 +174,3 @@ of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2021-2022 LNP/BP Standards Association, Switzerland - Copyright 2023-2024 UBIDECO Labs, - Institute for Distributed and Cognitive Computing, Switzerland. - - 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. diff --git a/MAINTAINERS.md b/MAINTAINERS.md index a3af1e0..a6792fc 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,6 +1,11 @@ -Maxim Orlovsky ---------------- +The project is maintained by Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +Institute for Distributed and Cognitive Systems (InDCS), Switzerland. + +List of the project maintainers: + +1. Maxim Orlovsky + - GitHub: [@dr-orlovsky](https://github.com/dr-orlovsky) - GPG: `EAE730CEC0C663763F028A5860094BAF18A26EC9` - SSH: `BoSGFzbyOKC7Jm28MJElFboGepihCpHop60nS8OoG/A` -- EMail: [dr@orlovsky.ch](mailto:dr@orlovsky.ch) +- EMail: [orlovsky@ubideco.org](mailto:orlovsky@ubideco.org) diff --git a/MANIFEST.yml b/MANIFEST.yml deleted file mode 100644 index ff5db7b..0000000 --- a/MANIFEST.yml +++ /dev/null @@ -1,14 +0,0 @@ -Name: strict_encoding -Type: Library -Kind: Free software -License: Apache-2.0 -Language: Rust -Compiler: 1.78 -Author: Maxim Orlovsky -Maintained: UBIDECO Labs, Institute for Deterministic and Cognitive Computing, Switzerland -Maintainers: - Maxim Orlovsky: - GitHub: @dr-orlovsky - GPG: EAE730CEC0C663763F028A5860094BAF18A26EC9 - SSH: BoSGFzbyOKC7Jm28MJElFboGepihCpHop60nS8OoG/A - EMail: dr@orlovsky.ch diff --git a/README.md b/README.md index 01e0e1f..b9417d1 100644 --- a/README.md +++ b/README.md @@ -10,42 +10,32 @@ [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) [![Apache-2 licensed](https://img.shields.io/crates/l/aluvm)](./LICENSE) -Rust implementation of AluVM (arithmetic logic unit virtual machine) and -assembler from Alu Assembly language into bytecode. - -AluVM is a pure functional register-based highly deterministic & -exception-less instruction set architecture (ISA) and virtual -machine (VM) without random memory access, capable of performing arithmetic -operations, including operations on elliptic curves. The AluVM ISA can be -extended by the environment running the virtual machine (host environment), -providing ability to load data to the VM registers and support -application-specific instructions (like SIMD). - -The main purpose for ALuVM is to be used in distributed systems whether -robustness, platform-independent determinism are more important than the -speed of computation. The main area of AluVM applications (using appropriate -ISA extensions) is blockchain environments, consensus-critical computations, -edge computing, multiparty computing (including deterministic machine learning), -client-side-validation, sandboxed computing and genetic algorithms. - -For more details on AluVM, please check [the specification][AluVM], watch -detailed presentation on [YouTube] or check [slides] from the presentation. +Rust implementation of AluVM (arithmetic logic unit virtual machine). + +AluVM is a pure functional register-based highly deterministic & exception-less instruction set architecture (ISA) and +virtual machine (VM). The AluVM ISA can be extended by an environment running the virtual machine (runtime environment), +providing ability to load data to the VM registers and support application-specific instructions (like SIMD). + +The main purpose for ALuVM is to be used in distributed systems whether robustness, platform-independent determinism are +more important than the speed of computation. The main area of AluVM applications (using appropriate ISA extensions) is +blockchain environments, consensus-critical computations, edge computing, multiparty computing (including deterministic +machine learning), client-side-validation, sandboxed computing and genetic algorithms. + +For more details on AluVM, please check [the specification][AluVM], watch detailed presentation on [YouTube] or +check [slides] from the presentation. ## Design -The robustness lies at the very core of AluVM. It is designed to avoid any -undefined behaviour. Specifically, +The robustness lies at the very core of AluVM. It is designed to avoid any undefined behaviour. Specifically, * All registers may be in the undefined state; -* Impossible/incorrect operations put destination register into a special - *undefined state*; -* Code always extended to 2^16 bytes with zeros, which corresponds to - “set st0 register to false and stop execution” op-code; +* Impossible/incorrect operations put destination register into a special *undefined state*; +* Code always extended to 2^16 bytes with zeros, which corresponds to “set st0 register to false and stop execution” + op-code; * There are no invalid jump operations; * There are no invalid instructions; * Cycles & jumps are counted with 2^16 limit (bounded-time execution); -* No ambiguity: any two distinct byte strings always represent strictly - distinct programs; +* No ambiguity: any two distinct byte strings always represent strictly distinct programs; * Code is always signed; * Data segment is always signed; * Code commits to the used ISA extensions; @@ -65,39 +55,33 @@ undefined behaviour. Specifically, * Control flow * Data load / movement between registers * ALU (including cryptography) -- Extensible with ISA extensions: 127 of the operations are reserved for - extensions +- Extensible with ISA extensions: 127 of the operations are reserved for extensions * More cryptography * Custom data I/O (blockchain, LN, client-side-validation) * Genetic algorithms / code self-modification The arithmetic ISA is designed with strong robustness goals: -- Impossible arithmetic operation (0/0, Inf/inf) always sets the destination - register into undefined state (unlike NaN in IEEE-754 it has only a single - unique value) -- Operation resulting in the value which can't fit the bit dimensions under a - used encoding, including representation of infinity for integer encodings - (x/0 if x != 0) results in: +- Impossible arithmetic operation (0/0, Inf/inf) always sets the destination register into undefined state (unlike NaN + in IEEE-754 it has only a single unique value) +- Operation resulting in the value which can't fit the bit dimensions under a used encoding, including representation of + infinity for integer encodings (x/0 if x != 0) results in: * for float underflows, subnormally encoded number, * for x/0 if x != 0 on float numbers, ±Inf float value, - * for overflows in integer checked operations and floats: undefined value, - setting st0 to false, - * for overflows in integer wrapped operations, modulo division on the maximum - register value + * for overflows in integer checked operations and floats: undefined value, setting st0 to false, + * for overflows in integer wrapped operations, modulo division on the maximum register value -Most of the arithmetic operations has to be provided with flags specifying -which of the encoding and exception handling should be used: +Most of the arithmetic operations has to be provided with flags specifying which of the encoding and exception handling +should be used: * Integer encodings has two flags: - one for signed/unsigned variant of the encoding - one for checked or wrapped variant of exception handling * Float encoding has 4 variants of rounding, matching IEEE-754 options -Thus, many arithmetic instructions have 8 variants, indicating the used -encoding (unsigned, signed integer or float) and operation behavior in -situation when resulting value does not fit into the register (overflow or -wrap for integers and one of four rounding options for floats). +Thus, many arithmetic instructions have 8 variants, indicating the used encoding (unsigned, signed integer or float) and +operation behavior in situation when resulting value does not fit into the register (overflow or wrap for integers and +one of four rounding options for floats). Check [the specification][AluVM] for the details. @@ -105,14 +89,12 @@ Check [the specification][AluVM] for the details. **ALU registers:** 8 blocks of 32 registers -- Integer arithmetic (A-registers) blocks: 8, 16, 32, 64, 128, 256, 512, - 1024 bits +- Integer arithmetic (A-registers) blocks: 8, 16, 32, 64, 128, 256, 512, 1024 bits - Float arithmetic (F-registers) blocks: * IEEE: binary-half, single, double, quad, oct precision * IEEE extension: 80-bit X87 register * BFloat16 register, used in Machine learning -- Cryptographic operations (R-registers) blocks: 128, 160, 256, 512, 1024, - 2048, 4096, 8192 bits +- Cryptographic operations (R-registers) blocks: 128, 160, 256, 512, 1024, 2048, 4096, 8192 bits - String registers (S-registers): 1 block of 256 registers, 64kb each **Control flow registers:** @@ -125,15 +107,13 @@ Check [the specification][AluVM] for the details. ## History -- The need for AluVM recognized as a part of RGB project in - Mar, the 24 & 31st, 2021 (see developers call ) -- Concept was presented on 19th of May 2021 - ([check the recoding](https://youtu.be/Mma0oyiVbSE)) +- The need for AluVM recognized as a part of RGB project in Mar, the 24 & 31st, 2021 (see developers call + ) +- Concept was presented on 19th of May 2021([check the recoding](https://youtu.be/Mma0oyiVbSE)) - v0.1 release of Rust AluVM implementation on the 28th of May 2021 ([ISA & API docs](https://docs.rs/aluvm/0.1.0/alure/)) -- v0.2 release with multiple enhancements on the 9 Jun 2021 - ([ISA & API docs](https://docs.rs/aluvm/0.2.1/aluvm/)) – see presentation - on [Youtube] or read [slides] +- v0.2 release with multiple enhancements on the 9 Jun 2021 ([ISA & API docs](https://docs.rs/aluvm/0.2.1/aluvm/)) – see + presentation on [YouTube] or read [slides] [AluVM]: https://github.com/AluVM/aluvm-spec diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 0000000..e75c4ab --- /dev/null +++ b/_typos.toml @@ -0,0 +1,13 @@ +[files] +# exclude the directory "stl/" +extend-exclude = ["stl/"] + +[default.extend-words] +# Don't correct the name "Jon Atack". +Atack = "Atack" + +[default] +extend-ignore-re = [ + # Don't correct URIs + "([a-z]+:)*[.~0-9A-Za-z-]+", +] diff --git a/codecov.yml b/codecov.yml index 240d731..453306a 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,12 +8,10 @@ coverage: status: project: default: - target: 75% threshold: 1% branches: - master patch: default: - target: 60% threshold: 1% only_pulls: true diff --git a/examples/rgb.rs b/examples/rgb.rs deleted file mode 100644 index 4e207cd..0000000 --- a/examples/rgb.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// Designed & written in 2021-2022 by -// Dr. Maxim Orlovsky -// This work is donated to LNP/BP Standards Association by Pandora Core AG -// -// This software is licensed under the terms of MIT License. -// You should have received a copy of the MIT License along with this software. -// If not, see . - -use aluvm::reg::{Reg32, RegA}; - -/// Example extension set of operations which are required for RGB -// TODO(#3) Move to RGB Core Library -pub enum RgbOp { - /// Counts number of metadata of specific type - CountMeta(u16, RegA, Reg32), - CountState(u16, RegA, Reg32), - CountRevealed(u16, RegA, Reg32), - CountPublic(u16, RegA, Reg32), - PullMeta( - /** State type */ u16, - /** Value index from `a16` register */ Reg32, - /** Destination start index */ Reg32, - /** Destination end index. If smaller that start, indexes are - * switched */ - Reg32, - /** Confidential or revealed */ bool, - ), - PullState( - /** State type */ u16, - /** Value index from `a16` register */ Reg32, - /** Destination start index */ Reg32, - /** Destination end index. If smaller that start, indexes are - * switched */ - Reg32, - /** Confidential or revealed */ bool, - ), - // We do not need the last two ops since they can be replaced with a - // library operations utilizing AluVM byte string opcodes - MatchMiniscript( - /** State type */ u16, - /** Miniscript string length */ u16, - /** Miniscript template in strict encoded format */ [u8; u64::MAX as usize], - ), - MatchPsbt( - /** State type */ u16, - /** Psbt string length */ u16, - /** Psbt template in strict encoded format */ [u8; u64::MAX as usize], - ), -} - -fn main() {} diff --git a/src/bin/aluvm-stl.rs b/src/bin/aluvm-stl.rs index d8b7263..f052f5c 100644 --- a/src/bin/aluvm-stl.rs +++ b/src/bin/aluvm-stl.rs @@ -3,25 +3,24 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. use aluvm::stl; use strict_types::typelib::parse_args; @@ -38,7 +37,9 @@ fn main() { " Description: AluVM data type library Author: Dr Maxim Orlovsky - Copyright (C) 2023-2024 UBIDECO Institute. All rights reserved. + Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), + Institute for Distributed and Cognitive Systems (InDCS), Switzerland. + Copyright (C) 2021-2025 Dr Maxim Orlovsky. License: Apache-2.0", ), ) diff --git a/src/core/core.rs b/src/core/core.rs new file mode 100644 index 0000000..ddd30c4 --- /dev/null +++ b/src/core/core.rs @@ -0,0 +1,251 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use core::fmt::{self, Debug, Formatter}; + +use amplify::confinement::ConfinedVec; + +use super::{Site, SiteId, Status}; +use crate::{Register, LIB_NAME_ALUVM}; + +/// Maximal size of call stack. +/// +/// Equals to 0xFFFF (i.e. maximum limited by `cy` and `cp` bit size). +pub const CALL_STACK_SIZE_MAX: u16 = 0xFF; + +pub trait CoreExt: Clone + Debug { + type Reg: Register; + type Config: Default; + + fn with(config: Self::Config) -> Self; + + fn get(&self, reg: Self::Reg) -> Option<::Value>; + fn clr(&mut self, reg: Self::Reg) -> Option<::Value>; + fn set( + &mut self, + reg: Self::Reg, + val: ::Value, + ) -> Option<::Value>; + + fn reset(&mut self); +} + +/// Registers of a single CPU/VM core. +#[derive(Clone)] +pub struct Core< + Id: SiteId, + Cx: CoreExt, + const CALL_STACK_SIZE: usize = { CALL_STACK_SIZE_MAX as usize }, +> { + /// Halt register. If set to `true`, halts program when `CK` is set to [`Status::Failed`] for + /// the first time. + /// + /// # See also + /// + /// - [`Core::ck`] register + /// - [`Core::cf`] register + pub(super) ch: bool, + + /// Check register, which is set on any failure (accessing register in `None` state, zero + /// division etc.). Can be reset. + /// + /// # See also + /// + /// - [`Core::ch`] register + /// - [`Core::cf`] register + pub(super) ck: Status, + + /// Failure register, which counts how many times `CK` was set, and can't be reset. + /// + /// # See also + /// + /// - [`Core::ch`] register + /// - [`Core::ck`] register + pub(super) cf: u64, + + /// Test register, which acts as boolean test result (also a carry flag). + pub(super) co: bool, + + /// Counts number of jumps (possible cycles). The number of jumps is limited by 2^16 per + /// script. + pub(super) cy: u16, + + /// Complexity accumulator / counter. + /// + /// Each instruction has associated computational complexity level. This register sums + /// complexity of executed instructions. + /// + /// # See also + /// + /// - [`Core::cy`] register + /// - [`Core::cl`] register + pub(super) ca: u64, + + /// Complexity limit. + /// + /// If this register has a value set, once [`Core::ca`] will reach this value the VM will + /// stop program execution setting `CK` to a failure. + pub(super) cl: Option, + + /// Call stack. + /// + /// # See also + /// + /// - [`CALL_STACK_SIZE_MAX`] constant + /// - [`Core::cp`] register + pub(super) cs: ConfinedVec, 0, CALL_STACK_SIZE>, + + /// Core extension module. + pub cx: Cx, +} + +/// Configuration for [`Core`] initialization. +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Debug)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_ALUVM)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CoreConfig { + /// Initial value for the `CH` register. + pub halt: bool, + /// Initial value for the `CL` register. + pub complexity_lim: Option, +} + +impl Default for CoreConfig { + /// Sets + /// - [`CoreConfig::halt`] to `true`, + /// - [`CoreConfig::complexity_lim`] to `None` + /// + /// # See also + /// + /// - [`CoreConfig::halt`] + /// - [`CoreConfig::complexity_lim`] + fn default() -> Self { CoreConfig { halt: true, complexity_lim: None } } +} + +impl Default + for Core +{ + fn default() -> Self { Core::new() } +} + +impl Core { + /// Initializes registers. Sets `st0` to `true`, counters to zero, call stack to empty and the + /// rest of registers to `None` value. + /// + /// An alias for [`Core::with`]`(`[`CoreConfig::default()`]`)`. + #[inline] + pub fn new() -> Self { + assert!(CALL_STACK_SIZE <= CALL_STACK_SIZE_MAX as usize, "Call stack size is too large"); + Core::with(default!(), default!()) + } + + /// Initializes registers using a configuration object [`CoreConfig`]. + pub fn with(config: CoreConfig, cx_config: Cx::Config) -> Self { + assert!(CALL_STACK_SIZE <= CALL_STACK_SIZE_MAX as usize, "Call stack size is too large"); + Core { + ch: config.halt, + ck: Status::Ok, + cf: 0, + co: false, + cy: 0, + ca: 0, + cl: config.complexity_lim, + cs: ConfinedVec::with_capacity(CALL_STACK_SIZE), + cx: Cx::with(cx_config), + } + } + + pub fn reset(&mut self) { + let mut new = Self::new(); + new.ch = self.ch; + new.cl = self.cl; + new.cx.reset(); + *self = new; + } +} + +impl Debug + for Core +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let (sect, reg, val, reset) = if f.alternate() { + ("\x1B[0;4;1m", "\x1B[0;1m", "\x1B[0;32m", "\x1B[0m") + } else { + ("", "", "", "") + }; + + writeln!(f, "{sect}C-regs:{reset}")?; + write!(f, "{reg}CH{reset} {val}{}{reset}, ", self.ch)?; + write!(f, "{reg}CK{reset} {val}{}{reset}, ", self.ck)?; + write!(f, "{reg}CF{reset} {val}{}{reset}, ", self.cf)?; + write!(f, "{reg}CO{reset} {val}{}{reset}, ", self.co)?; + write!(f, "{reg}CY{reset} {val}{}{reset}, ", self.cy)?; + write!(f, "{reg}CA{reset} {val}{}{reset}, ", self.ca)?; + let cl = self + .cl + .map(|v| v.to_string()) + .unwrap_or_else(|| "~".to_string()); + write!(f, "{reg}CL{reset} {val}{cl}{reset}, ")?; + write!(f, "{reg}CP{reset} {val}{}{reset}, ", self.cp())?; + write!(f, "\n{reg}CS{reset} {val}{reset}")?; + for item in &self.cs { + write!(f, "{} ", item)?; + } + writeln!(f)?; + + Debug::fmt(&self.cx, f) + } +} + +impl Core { + pub fn from(core: Core) -> Self + where Cx: From { + Core { + ch: core.ch, + ck: core.ck, + cf: core.cf, + co: core.co, + cy: core.cy, + ca: core.ca, + cl: core.cl, + cs: core.cs, + cx: core.cx.into(), + } + } + + pub fn extend(self, cx: Cx2) -> Core { + Core { + ch: self.ch, + ck: self.ck, + cf: self.cf, + co: self.co, + cy: self.cy, + ca: self.ca, + cl: self.cl, + cs: self.cs, + cx, + } + } +} diff --git a/src/core/microcode.rs b/src/core/microcode.rs new file mode 100644 index 0000000..62a5862 --- /dev/null +++ b/src/core/microcode.rs @@ -0,0 +1,88 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use crate::core::{Core, CoreExt, SiteId, Status}; +use crate::{Register, Site}; + +/// Microcode for flag registers. +impl Core { + /// Read overflow/carry flag. + pub fn co(&self) -> bool { self.co } + + /// Set overflow/carry flag to a value. + pub fn set_co(&mut self, co: bool) { self.co = co; } + + /// Return how many times `ck` was set to a failed state. + pub fn cf(&self) -> u64 { self.cf } + + /// Return `true` if `ck` was in a failed state for at least once. + pub fn has_failed(&self) -> bool { self.cf > 0 } + + /// Return whether check register `ck` is in a failed state. + pub fn ck(&self) -> Status { self.ck } + + /// Set `CK` register to a failed state. + /// + /// Returns whether further execution should be stopped (i.e. `ch` register value). + #[must_use] + pub fn fail_ck(&mut self) -> bool { + self.ck = Status::Fail; + self.cf += 1; + self.ch + } + + /// Reset `CK` register. + pub fn reset_ck(&mut self) { self.ck = Status::Ok } + + /// Return size of the call stack. + pub fn cp(&self) -> u16 { self.cs.len() as u16 } + + /// Push a location to a call stack. + /// + /// # Returns + /// + /// Top of the call stack. + pub fn push_cs(&mut self, from: Site) -> Option { + self.cs.push(from).ok()?; + Some(self.cp()) + } + + /// Pops a call stack item. + pub fn pop_cs(&mut self) -> Option> { self.cs.pop() } + + /// Return complexity limit value. + pub fn cl(&self) -> Option { self.cl } + + /// Accumulate complexity value. + /// + /// # Returns + /// + /// Boolean indicating wheather complexity limit is reached. + pub fn acc_complexity(&mut self, complexity: u64) -> bool { + self.ca = self.ca.saturating_add(complexity); + self.cl().map(|lim| self.ca >= lim).unwrap_or_default() + } + + pub fn get(&self, reg: Cx::Reg) -> Option<::Value> { self.cx.get(reg) } +} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..0d6eb47 --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,33 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +//! AluVM registers system + +#[allow(clippy::module_inception)] +mod core; +mod microcode; +mod util; + +pub use self::core::{Core, CoreConfig, CoreExt, CALL_STACK_SIZE_MAX}; +pub use self::util::{NoExt, NoRegs, Register, Site, SiteId, Status}; diff --git a/src/core/util.rs b/src/core/util.rs new file mode 100644 index 0000000..d6fb95d --- /dev/null +++ b/src/core/util.rs @@ -0,0 +1,113 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use core::fmt::{self, Debug, Display, Formatter}; +use core::str::FromStr; +use std::cmp::Ordering; + +use crate::core::CoreExt; + +pub trait Register: Copy + Ord + Debug + Display { + type Value: Copy + Debug + Display; + fn bytes(self) -> u16; +} + +#[derive(Debug)] +pub enum NoRegs {} +#[allow(clippy::non_canonical_clone_impl)] +impl Clone for NoRegs { + fn clone(&self) -> Self { unreachable!() } +} +impl Copy for NoRegs {} +#[allow(clippy::non_canonical_clone_impl)] +impl PartialEq for NoRegs { + fn eq(&self, _: &Self) -> bool { unreachable!() } +} +impl Eq for NoRegs {} +impl Ord for NoRegs { + fn cmp(&self, _: &Self) -> Ordering { unreachable!() } +} +#[allow(clippy::non_canonical_partial_ord_impl)] +impl PartialOrd for NoRegs { + fn partial_cmp(&self, _: &Self) -> Option { unreachable!() } +} +impl Display for NoRegs { + fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { unreachable!() } +} +impl Register for NoRegs { + type Value = u8; + fn bytes(self) -> u16 { unreachable!() } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display)] +#[repr(i8)] +pub enum Status { + #[display("ok")] + Ok = 0, + + #[display("fail")] + Fail = -1, +} + +impl Status { + pub fn is_ok(self) -> bool { self == Status::Ok } +} + +pub trait SiteId: Copy + Ord + Debug + Display + FromStr {} + +/// Location inside the instruction sequence which can be executed by the core. +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] +pub struct Site { + pub prog_id: Id, + pub offset: u16, +} + +impl Site { + #[inline] + pub fn new(prog_id: Id, offset: u16) -> Self { Self { prog_id, offset } } +} + +impl Display for Site { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}@{:04X}#h", self.prog_id, self.offset) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct NoExt; + +impl CoreExt for NoExt { + type Reg = NoRegs; + type Config = (); + + fn with(_config: Self::Config) -> Self { NoExt } + + fn get(&self, _reg: Self::Reg) -> Option { None } + + fn clr(&mut self, _reg: Self::Reg) -> Option { None } + + fn set(&mut self, _reg: Self::Reg, _val: u8) -> Option { None } + + fn reset(&mut self) {} +} diff --git a/src/data/arithm.rs b/src/data/arithm.rs deleted file mode 100644 index a680e09..0000000 --- a/src/data/arithm.rs +++ /dev/null @@ -1,986 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -use core::cmp::Ordering; -use core::ops::{Neg, Rem}; - -use amplify::num::apfloat::{ieee, Float}; -use half::bf16; - -use super::{FloatLayout, IntLayout, Layout, Number, NumberLayout}; -use crate::data::MaybeNumber; -use crate::isa::{IntFlags, RoundingFlag}; - -impl PartialEq for Number { - #[inline] - fn eq(&self, other: &Self) -> bool { - (self.layout() == other.layout() - || (self.layout().is_signed_int() && other.layout().is_unsigned_int()) - || (self.layout().is_unsigned_int() && other.layout().is_signed_int())) - && self.to_clean()[..].eq(&other.to_clean()[..]) - } -} - -impl Eq for Number {} - -impl PartialOrd for Number { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} - -/// Since we always convert `NaN` values into `None` and keep them at the level of `MaybeNumber`, we -/// can do strict ordering even on float numbers -impl Ord for Number { - fn cmp(&self, other: &Self) -> Ordering { - assert_eq!(self.layout(), other.layout(), "comparing numbers with different layout"); - match self.layout() { - Layout::Integer(_) => match (self.is_positive(), other.is_positive()) { - (true, false) => Ordering::Greater, - (false, true) => Ordering::Less, - _ => self.to_u1024_bytes().cmp(&other.to_u1024_bytes()), - }, - Layout::Float(FloatLayout::BFloat16) => { - bf16::from(self).partial_cmp(&bf16::from(other)).expect("number value contains NaN") - } - Layout::Float(FloatLayout::IeeeHalf) => ieee::Half::from(self) - .partial_cmp(&ieee::Half::from(other)) - .expect("number value contains NaN"), - Layout::Float(FloatLayout::IeeeSingle) => ieee::Single::from(self) - .partial_cmp(&ieee::Single::from(other)) - .expect("number value contains NaN"), - Layout::Float(FloatLayout::IeeeDouble) => ieee::Double::from(self) - .partial_cmp(&ieee::Double::from(other)) - .expect("number value contains NaN"), - Layout::Float(FloatLayout::X87DoubleExt) => ieee::X87DoubleExtended::from(self) - .partial_cmp(&ieee::X87DoubleExtended::from(other)) - .expect("number value contains NaN"), - Layout::Float(FloatLayout::IeeeQuad) => ieee::Quad::from(self) - .partial_cmp(&ieee::Quad::from(other)) - .expect("number value contains NaN"), - Layout::Float(FloatLayout::IeeeOct) => { - unimplemented!("IEEE-754 256-bit floats are not yet supported") - } - Layout::Float(FloatLayout::FloatTapered) => { - unimplemented!("512-bit tapered floats are not yet supported") - } - } - } -} - -impl Number { - /// Does comparison by ignoring the difference in the last bit of significand for float layouts. - /// For integers performs normal comparison. - pub fn rounding_cmp(&self, other: &Self) -> Ordering { - assert_eq!(self.layout(), other.layout(), "comparing numbers with different layout"); - match self.layout() { - Layout::Integer(_) => self.cmp(other), - Layout::Float(FloatLayout::FloatTapered) => { - unimplemented!("512-bit tapered floats are not yet supported") - } - Layout::Float(float_layout) => { - let last_bit = Number::masked_bit( - float_layout - .significand_pos() - .expect("non-tapered float layout does not provides significand position") - .end, - self.layout(), - ); - (*self ^ last_bit).cmp(&(*other ^ last_bit)) - } - } - } - - /// Checks for the equality ignoring the difference in the last bit of significand for float - /// layouts. For integers performs normal comparison. - #[inline] - pub fn rounding_eq(&self, other: &Self) -> bool { self.rounding_cmp(other) == Ordering::Equal } -} - -impl Number { - /// Addition of two integers with configuration flags for overflow and signed format. - /// If `signed` flag is inconsistent with Number layout, - /// the layout will be discarded before computing. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn int_add(self, rhs: Self, flags: IntFlags) -> Option { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "adding numbers with different layout"); - match (layout, flags.signed) { - (Layout::Integer(IntLayout { bytes, .. }), true) => self - .to_i1024_bytes() - .checked_add(rhs.to_i1024_bytes()) - .map(Number::from) - .and_then(|n| n.reshaped(Layout::signed(n.layout().bytes()), true)) - .and_then(|mut n| (n.reshape(Layout::signed(bytes)) || flags.wrap).then_some(n)), - (Layout::Integer(IntLayout { bytes, .. }), false) => self - .to_u1024_bytes() - .checked_add(rhs.to_u1024_bytes()) - .map(Number::from) - .and_then(|n| n.reshaped(Layout::unsigned(n.layout().bytes()), true)) - .and_then(|mut n| (n.reshape(Layout::unsigned(bytes)) || flags.wrap).then_some(n)), - (Layout::Float(_), _) => panic!("integer addition of float numbers"), - } - } - - /// Subtraction of two integers with configuration flags for overflow and signed format. - /// If `signed` flag is inconsistent with Number layout, - /// the layout will be discarded before computing. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn int_sub(self, rhs: Self, flags: IntFlags) -> Option { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "subtracting numbers with different layout"); - match (layout, flags.signed) { - (Layout::Integer(IntLayout { bytes, .. }), true) => self - .to_i1024_bytes() - .checked_sub(rhs.to_i1024_bytes()) - .map(Number::from) - .and_then(|n| n.reshaped(Layout::signed(n.layout().bytes()), true)) - .and_then(|mut n| (n.reshape(Layout::signed(bytes)) || flags.wrap).then_some(n)), - (Layout::Integer(IntLayout { bytes, .. }), false) => self - .to_u1024_bytes() - .checked_sub(rhs.to_u1024_bytes()) - .map(Number::from) - .and_then(|n| n.reshaped(Layout::unsigned(n.layout().bytes()), true)) - .and_then(|mut n| (n.reshape(Layout::unsigned(bytes)) || flags.wrap).then_some(n)), - (Layout::Float(_), _) => panic!("integer subtraction of float numbers"), - } - } - - /// Multiplication of two integers with configuration flags for overflow and signed format. - /// If `signed` flag is inconsistent with Number layout, - /// the layout will be discarded before computing. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn int_mul(self, rhs: Self, flags: IntFlags) -> Option { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "multiplying numbers with different layout"); - match (layout, flags.signed) { - (Layout::Integer(IntLayout { bytes, .. }), true) => self - .to_i1024_bytes() - .checked_mul(rhs.to_i1024_bytes()) - .map(Number::from) - .and_then(|n| n.reshaped(Layout::signed(n.layout().bytes()), true)) - .and_then(|mut n| (n.reshape(Layout::signed(bytes)) || flags.wrap).then_some(n)), - (Layout::Integer(IntLayout { bytes, .. }), false) => self - .to_u1024_bytes() - .checked_mul(rhs.to_u1024_bytes()) - .map(Number::from) - .and_then(|n| n.reshaped(Layout::unsigned(n.layout().bytes()), true)) - .and_then(|mut n| (n.reshape(Layout::unsigned(bytes)) || flags.wrap).then_some(n)), - (Layout::Float(_), _) => panic!("integer multiplication of float numbers"), - } - } - - /// Division of two integers with configuration flags for Euclidean division and signed format. - /// If `signed` flag is inconsistent with Number layout, - /// the layout will be discarded before computing. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn int_div(self, rhs: Self, flags: IntFlags) -> Option { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "dividing numbers with different layout"); - - if rhs.is_zero() { - return None; - } - - if self.is_zero() { - return Some(Number::zero(layout)); - } - - match (layout, flags.signed) { - (Layout::Integer(IntLayout { bytes, .. }), true) => { - let res = match flags.wrap { - true => self.to_i1024_bytes().checked_div_euclid(rhs.to_i1024_bytes()), - false => self.to_i1024_bytes().checked_div(rhs.to_i1024_bytes()), - }; - res.map(Number::from) - .and_then(|n| n.reshaped(Layout::signed(n.layout().bytes()), true)) - .and_then(|n| n.reshaped(Layout::signed(bytes), false)) - } - (Layout::Integer(IntLayout { bytes, .. }), false) => self - .to_u1024_bytes() - .checked_div(rhs.to_u1024_bytes()) - .map(Number::from) - .and_then(|n| n.reshaped(Layout::signed(bytes), false)), - (Layout::Float(_), _) => panic!("integer division of float numbers"), - } - } - - /// Addition of two floats with configuration flags for rounding. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn float_add(self, rhs: Self, flag: RoundingFlag) -> MaybeNumber { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "adding numbers with different layout"); - match layout { - Layout::Float(FloatLayout::BFloat16) => (bf16::from(self) + bf16::from(rhs)).into(), - Layout::Float(FloatLayout::IeeeHalf) => { - ieee::Half::from(self).add_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeSingle) => { - ieee::Single::from(self).add_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeDouble) => { - ieee::Double::from(self).add_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeQuad) => { - ieee::Quad::from(self).add_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::X87DoubleExt) => { - ieee::X87DoubleExtended::from(self).add_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeOct) => { - ieee::Oct::from(self).add_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::FloatTapered) => todo!("(#5) tapered float addition"), - Layout::Integer(_) => panic!("float addition of integer numbers"), - } - } - - /// Subtraction of two floats with configuration flags for rounding. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn float_sub(self, rhs: Self, flag: RoundingFlag) -> MaybeNumber { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "subtracting numbers with different layout"); - match layout { - Layout::Float(FloatLayout::BFloat16) => (bf16::from(self) - bf16::from(rhs)).into(), - Layout::Float(FloatLayout::IeeeHalf) => { - ieee::Half::from(self).sub_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeSingle) => { - ieee::Single::from(self).sub_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeDouble) => { - ieee::Double::from(self).sub_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeQuad) => { - ieee::Quad::from(self).sub_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::X87DoubleExt) => { - ieee::X87DoubleExtended::from(self).sub_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeOct) => { - ieee::Oct::from(self).sub_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::FloatTapered) => todo!("(#5) tapered float subtraction"), - Layout::Integer(_) => panic!("float subtraction of integer numbers"), - } - } - - /// Multiplication of two floats with configuration flags for rounding. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn float_mul(self, rhs: Self, flag: RoundingFlag) -> MaybeNumber { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "multiplying numbers with different layout"); - match layout { - Layout::Float(FloatLayout::BFloat16) => (bf16::from(self) * bf16::from(rhs)).into(), - Layout::Float(FloatLayout::IeeeHalf) => { - ieee::Half::from(self).mul_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeSingle) => { - ieee::Single::from(self).mul_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeDouble) => { - ieee::Double::from(self).mul_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeQuad) => { - ieee::Quad::from(self).mul_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::X87DoubleExt) => { - ieee::X87DoubleExtended::from(self).mul_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeOct) => { - ieee::Oct::from(self).mul_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::FloatTapered) => todo!("(#5) tapered float multiplication"), - Layout::Integer(_) => panic!("float multiplication of integer numbers"), - } - } - - /// Division of two floats with configuration flags for rounding. - /// - /// # Panics - /// - /// - if applied to float number layouts - /// - if numbers in arguments has different layout. - pub fn float_div(self, rhs: Self, flag: RoundingFlag) -> MaybeNumber { - let layout = self.layout(); - assert_eq!(layout, rhs.layout(), "dividing numbers with different layout"); - match layout { - Layout::Float(FloatLayout::BFloat16) => (bf16::from(self) / bf16::from(rhs)).into(), - Layout::Float(FloatLayout::IeeeHalf) => { - ieee::Half::from(self).div_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeSingle) => { - ieee::Single::from(self).div_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeDouble) => { - ieee::Double::from(self).div_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeQuad) => { - ieee::Quad::from(self).div_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::X87DoubleExt) => { - ieee::X87DoubleExtended::from(self).div_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::IeeeOct) => { - ieee::Oct::from(self).div_r(rhs.into(), flag.into()).into() - } - Layout::Float(FloatLayout::FloatTapered) => todo!("(#5) tapered float division"), - Layout::Integer(_) => panic!("float division of integer numbers"), - } - } - - /// Adds or removes negative sign to the number (negates negative or positive number, depending - /// on the method argument value) - /// - /// # Returns - /// Result of the operation as an optional - or `None` if the operation was impossible, - /// specifically: - /// - applied to unsigned integer layout - /// - an attempt to negate the minimum possible value for its layout (e.g. -128 as 1 byte) - #[inline] - pub fn applying_sign(mut self, sign: impl Into) -> Option { - let layout = self.layout(); - match layout { - Layout::Integer(IntLayout { signed: true, .. }) => { - if !self.is_positive() && self.count_ones() == 1 { - // attempt to negate the minimum possible value for its layout - None - } else if !self.is_positive() ^ sign.into() { - let mut one = Number::from(1u8); - one.reshape(layout); - (!self).int_add(one, IntFlags { - signed: true, - wrap: true, - }) - } else { - Some(self) - } - } - Layout::Integer(IntLayout { signed: false, .. }) => { - // applied to unsigned integer layout - None - } - Layout::Float(..) => { - let sign_byte = layout.sign_byte(); - if sign.into() { - self[sign_byte] |= 0x80; - } else { - self[sign_byte] &= 0x7F; - } - Some(self) - } - } - } - - /// Removes negative sign if present (negates negative number) - #[inline] - pub fn without_sign(self) -> Option { self.applying_sign(false) } -} - -impl Rem for Number { - type Output = Option; - - fn rem(self, rhs: Self) -> Self::Output { - if rhs.is_zero() { - return None; - } - let layout = self.layout(); - Some(match layout { - Layout::Integer(IntLayout { signed: true, .. }) => { - let val1 = self.to_u1024_bytes(); - let val2 = rhs.to_u1024_bytes(); - val1.rem(val2).into() - } - Layout::Integer(IntLayout { signed: false, .. }) if layout.bits() <= 128 => { - let val1 = i128::from(self); - let val2 = i128::from(rhs); - val1.rem(val2).into() - } - Layout::Integer(IntLayout { .. }) => { - todo!("(#11) implement large signed number modulo division algorithm") - } - Layout::Float(_) => panic!("modulo division of float number"), - }) - } -} - -impl Neg for Number { - type Output = Option; - - fn neg(self) -> Self::Output { self.applying_sign(self.is_positive()) } -} - -impl Number { - /// Returns the absolute value of the number - pub fn abs(self) -> Option { - if self.is_positive() { - Some(self) - } else { - self.applying_sign(false) - } - } -} - -#[cfg(test)] -mod tests { - use core::str::FromStr; - - use super::*; - - #[test] - fn compare_numbers() { - let x = Number::from(0); - let y = Number::from(0); - assert_eq!(x, y); - let x = Number::from(0); - let y = Number::from(1); - assert!(x < y); - let x = Number::from(1); - let y = Number::from(-1); - assert!(x > y); - let x = Number::from(-128i8); - let y = Number::from(-127i8); - assert!(x < y); - } - - #[test] - fn int_add() { - let x = Number::from(1); - let y = Number::from(2); - let z = Number::from(3); - assert_eq!( - x.int_add(y, IntFlags { - signed: false, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_add(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - let x = Number::from(255u8); - let y = Number::from(1u8); - let z = Number::from(0u8); - assert_eq!( - x.int_add(y, IntFlags { - signed: false, - wrap: false - }), - None - ); - assert_eq!( - x.int_add(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_add(y, IntFlags { - signed: false, - wrap: true - }), - Some(z) - ); - let x = Number::from(1i8); - let y = Number::from(-1i8); - let z = Number::from(0i8); - assert_eq!( - x.int_add(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_add(y, IntFlags { - signed: true, - wrap: true - }), - Some(z) - ); - assert_eq!( - x.int_add(y, IntFlags { - signed: false, - wrap: false - }), - None - ); - let x = Number::from(-2i8); - let y = Number::from(-1i8); - let z = Number::from(-3i8); - assert_eq!( - x.int_add(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_add(y, IntFlags { - signed: true, - wrap: true - }), - Some(z) - ); - assert_eq!( - x.int_add(y, IntFlags { - signed: false, - wrap: false - }), - None - ); - } - - #[test] - fn int_sub() { - let x = Number::from(3u8); - let y = Number::from(2u8); - let z = Number::from(1u8); - assert_eq!( - x.int_sub(y, IntFlags { - signed: false, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_sub(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - let x = Number::from(0i8); - let y = Number::from(42i8); - let z = Number::from(-42i8); - assert_eq!( - x.int_sub(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_sub(y, IntFlags { - signed: false, - wrap: false - }), - None - ); - let x = Number::from(6000); - let y = Number::from(5000); - let z = Number::from(1000); - assert_eq!( - x.int_sub(y, IntFlags { - signed: false, - wrap: false - }), - Some(z) - ); - let x = Number::from(-10i8); - let y = Number::from(-4i8); - let z = Number::from(-6i8); - let w = Number::from(6u8); - assert_eq!( - x.int_sub(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_sub(y, IntFlags { - signed: false, - wrap: false - }), - None - ); - // 246 - 252 - assert_eq!( - x.int_sub(y, IntFlags { - signed: false, - wrap: true - }), - None - ); - assert_eq!( - y.int_sub(x, IntFlags { - signed: false, - wrap: true - }), - Some(w) - ); - } - - #[test] - fn int_mul() { - let x = Number::from(2); - let y = Number::from(3); - let z = Number::from(6); - assert_eq!( - x.int_mul(y, IntFlags { - signed: false, - wrap: false - }), - Some(z) - ); - let x = Number::from(128u8); - let y = Number::from(2u8); - let z = Number::from(0u8); - assert_eq!( - x.int_mul(y, IntFlags { - signed: false, - wrap: false - }), - None - ); - assert_eq!( - x.int_mul(y, IntFlags { - signed: false, - wrap: true - }), - Some(z) - ); - let x = Number::from(4); - let y = Number::from(0); - let z = Number::from(0); - assert_eq!( - x.int_mul(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - let x = Number::from(-2); - let y = Number::from(-5); - let z = Number::from(10); - assert_eq!( - x.int_mul(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_mul(y, IntFlags { - signed: true, - wrap: true - }), - Some(z) - ); - } - - #[test] - fn int_div() { - let x = Number::from(6); - let y = Number::from(3); - let z = Number::from(2); - assert_eq!( - x.int_div(y, IntFlags { - signed: false, - wrap: false - }), - Some(z) - ); - let x = Number::from(7); - let y = Number::from(2); - let z = Number::from(3); - assert_eq!( - x.int_div(y, IntFlags { - signed: false, - wrap: false - }), - Some(z) - ); - let x = Number::from(4u8); - let y = Number::from(0u8); - assert_eq!( - x.int_div(y, IntFlags { - signed: false, - wrap: false - }), - None - ); - assert_eq!( - x.int_div(y, IntFlags { - signed: false, - wrap: true - }), - None - ); - assert_eq!( - x.int_div(y, IntFlags { - signed: true, - wrap: false - }), - None - ); - assert_eq!( - x.int_div(y, IntFlags { - signed: true, - wrap: true - }), - None - ); - let x = Number::from(-7i8); - let y = Number::from(4i8); - let z = Number::from(-1i8); - let w = Number::from(-2i8); - assert_eq!( - x.int_div(y, IntFlags { - signed: true, - wrap: false - }), - Some(z) - ); - assert_eq!( - x.int_div(y, IntFlags { - signed: true, - wrap: true - }), - Some(w) - ); - let x = Number::from(-128i8); - let y = Number::from(-1i8); - let z = Number::from(0i8); - assert_eq!( - x.int_div(y, IntFlags { - signed: true, - wrap: false - }), - None - ); - assert_eq!( - x.int_div(y, IntFlags { - signed: false, - wrap: true - }), - Some(z) - ); - } - - #[test] - fn applying_sign() { - let x = Number::from(1i8); - let y = Number::from(-1i8); - assert_eq!(x.applying_sign(true).unwrap(), y); - assert_eq!(x, y.applying_sign(false).unwrap()); - - let x = Number::from(1i8); - let y = Number::from(1i8); - assert_eq!(x.applying_sign(false).unwrap(), y); - - let x = MaybeNumber::from(bf16::from_f32(7.0_f32)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(-7.0_f32)).unwrap(); - assert_ne!(x, y); - assert_eq!(x.applying_sign(true).unwrap(), y); - - let x = MaybeNumber::from(bf16::from_f32(7.0_f32)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(7.0_f32)).unwrap(); - assert_eq!(x, y); - assert_eq!(x.applying_sign(false).unwrap(), y); - } - - #[test] - fn applying_sign_to_unsigned() { - let x = Number::from(1u8); - assert_eq!(None, x.applying_sign(false)); - } - - #[test] - fn applying_sign_to_minimum() { - let x = Number::from(-128i8); - assert_eq!(None, x.applying_sign(false)); - } - - #[test] - fn without_sign() { - let x = Number::from(1i8); - let y = Number::from(-1i8); - assert_eq!(x.without_sign().unwrap(), x); - assert_eq!(y.without_sign().unwrap(), x); - } - - #[test] - fn neg() { - let x = Number::from(1i8); - let y = Number::from(-1i8); - assert_ne!(x, y); - assert_eq!(x.neg().unwrap(), y); - assert_eq!(x, y.neg().unwrap()); - - let x = MaybeNumber::from(bf16::from_f32(7.0_f32)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(-7.0_f32)).unwrap(); - assert_ne!(x, y); - assert_eq!(x.neg().unwrap(), y); - assert_eq!(x, y.neg().unwrap()); - } - - #[test] - fn abs() { - let x = Number::from(1i8); - let y = Number::from(-1i8); - assert_eq!(x, y.abs().unwrap()); - - let x = MaybeNumber::from(bf16::from_f32(12.3_f32)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(-12.3_f32)).unwrap(); - assert_eq!(x, y.abs().unwrap()); - - let x = Number::from(-128i8); - assert_eq!(None, x.abs()); - - let x = Number::from(-128i16); - let y = Number::from(128i16); - assert_eq!(x.abs().unwrap(), y); - } - - #[test] - fn float_add() { - let x = MaybeNumber::from(ieee::Single::from_str("0x1p+0").unwrap()).unwrap(); - let y = MaybeNumber::from(ieee::Single::from_str("0x1p+0").unwrap()).unwrap(); - let z = MaybeNumber::from(ieee::Single::from_str("0x1p+1").unwrap()); - assert_eq!(x.float_add(y, RoundingFlag::Ceil), z); - - let x = MaybeNumber::from(ieee::Single::from_str("0x1p+0").unwrap()).unwrap(); - let y = MaybeNumber::from(ieee::Single::ZERO); - assert_eq!(x.float_add((-x).unwrap(), RoundingFlag::Ceil), y); - - // overflow - let x = MaybeNumber::from(ieee::Single::largest()).unwrap(); - let y = MaybeNumber::from(ieee::Single::from_str("0x1p+0").unwrap()).unwrap(); - assert_eq!(x.float_add(y, RoundingFlag::Ceil), MaybeNumber::none()); - } - - #[test] - fn float_sub() { - let x = MaybeNumber::from(ieee::Oct::from_str("0x1p+1").unwrap()).unwrap(); - let y = MaybeNumber::from(ieee::Oct::from_str("0x1p+0").unwrap()).unwrap(); - let z = MaybeNumber::from(ieee::Oct::from_str("0x1p+0").unwrap()); - assert_eq!(x.float_sub(y, RoundingFlag::Ceil), z); - - // INF - (-INF) = INF - let x = MaybeNumber::from(ieee::Single::INFINITY).unwrap(); - assert_eq!(x.float_sub((-x).unwrap(), RoundingFlag::Ceil), MaybeNumber::from(x)); - - // INF - INF = NaN - let x = MaybeNumber::from(ieee::Single::INFINITY).unwrap(); - assert_eq!(x.float_sub(x, RoundingFlag::Ceil), MaybeNumber::none()); - } - - #[test] - fn float_mul() { - let x = MaybeNumber::from(ieee::Single::from_str("0x1p+1").unwrap()).unwrap(); - let y = MaybeNumber::from(ieee::Single::from_str("0x1p+2").unwrap()).unwrap(); - let z = MaybeNumber::from(ieee::Single::from_str("0x1p+3").unwrap()); - assert_eq!(x.float_mul(y, RoundingFlag::Ceil), z); - } - - #[test] - fn float_div() { - let x = MaybeNumber::from(ieee::Single::from_str("0x1p+0").unwrap()).unwrap(); - let y = MaybeNumber::from(ieee::Single::from_str("0x2p+0").unwrap()).unwrap(); - let z = MaybeNumber::from(ieee::Single::from_str("0x1p-1").unwrap()); - assert_eq!(x.float_div(y, RoundingFlag::Ceil), z); - let x = MaybeNumber::from(ieee::Single::from_str("0x1p+0").unwrap()).unwrap(); - let y = MaybeNumber::from(ieee::Single::ZERO).unwrap(); - assert_eq!(x.float_div(y, RoundingFlag::Ceil), MaybeNumber::none()); - } - - #[test] - fn bf16_add() { - let x = MaybeNumber::from(bf16::from_f32(0.5)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(1.)).unwrap(); - let z = MaybeNumber::from(bf16::from_f32(1.5)); - assert_eq!(x.float_add(y, RoundingFlag::Ceil), z); - - let x = MaybeNumber::from(bf16::from_f32(0.5)).unwrap(); - let y = MaybeNumber::from(bf16::ZERO); - assert_eq!(x.float_add((-x).unwrap(), RoundingFlag::Ceil), y); - - // will not overflow - let x = MaybeNumber::from(bf16::MAX).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(1.)).unwrap(); - assert_eq!(x.float_add(y, RoundingFlag::Ceil), MaybeNumber::from(x)); - } - - #[test] - fn bf16_sub() { - let x = MaybeNumber::from(bf16::from_f32(0.5)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(1.)).unwrap(); - let z = MaybeNumber::from(bf16::from_f32(-0.5)); - assert_eq!(x.float_sub(y, RoundingFlag::Ceil), z); - } - - #[test] - fn bf16_mul() { - let x = MaybeNumber::from(bf16::from_f32(2.5)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(2.)).unwrap(); - let z = MaybeNumber::from(bf16::from_f32(5.0)); - assert_eq!(x.float_mul(y, RoundingFlag::Ceil), z); - } - - #[test] - fn bf16_div() { - let x = MaybeNumber::from(bf16::from_f32(6.)).unwrap(); - let y = MaybeNumber::from(bf16::from_f32(2.)).unwrap(); - let z = MaybeNumber::from(bf16::from_f32(3.)); - assert_eq!(x.float_div(y, RoundingFlag::Ceil), z); - let x = MaybeNumber::from(bf16::from_f32(6.)).unwrap(); - let y = MaybeNumber::from(bf16::ZERO).unwrap(); - let z = MaybeNumber::from(bf16::INFINITY); - assert_eq!(x.float_div(y, RoundingFlag::Ceil), z); - } -} diff --git a/src/data/bitwise.rs b/src/data/bitwise.rs deleted file mode 100644 index 85b5b2a..0000000 --- a/src/data/bitwise.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; - -use amplify::num::{i1024, u1024}; - -use super::{MaybeNumber, Number}; - -impl Not for MaybeNumber { - type Output = MaybeNumber; - - #[inline] - fn not(self) -> Self::Output { self.map(Number::not).into() } -} - -impl Not for Number { - type Output = Number; - - #[inline] - fn not(mut self) -> Self::Output { - for i in 0..self.len() { - self[i] = !self[i]; - } - self - } -} - -impl BitAnd for Number { - type Output = Number; - - #[inline] - fn bitand(self, rhs: Self) -> Self::Output { - self.to_u1024_bytes().bitand(rhs.to_u1024_bytes()).into() - } -} - -impl BitOr for Number { - type Output = Number; - - #[inline] - fn bitor(self, rhs: Self) -> Self::Output { - self.to_u1024_bytes().bitor(rhs.to_u1024_bytes()).into() - } -} - -impl BitXor for Number { - type Output = Number; - - #[inline] - fn bitxor(self, rhs: Self) -> Self::Output { - self.to_u1024_bytes().bitxor(rhs.to_u1024_bytes()).into() - } -} - -impl Shl for Number { - type Output = Number; - - #[inline] - fn shl(self, rhs: Self) -> Self::Output { - let layout = self.layout(); - assert!(layout.is_integer(), "bit shifting float number"); - let rhs = u16::from(rhs); - let mut n = match layout.is_signed_int() { - true => { - Number::from(self.to_i1024_bytes().checked_shl(rhs as u32).unwrap_or(i1024::ZERO)) - } - false => { - Number::from(self.to_u1024_bytes().checked_shl(rhs as u32).unwrap_or(u1024::ZERO)) - } - }; - n.reshape(layout); - n - } -} - -impl Shr for Number { - type Output = Number; - - #[inline] - fn shr(self, rhs: Self) -> Self::Output { - let layout = self.layout(); - assert!(layout.is_integer(), "bit shifting float number"); - let rhs = u16::from(rhs); - let mut n = match layout.is_signed_int() { - true => { - Number::from(self.to_i1024_bytes().checked_shr(rhs as u32).unwrap_or(i1024::ZERO)) - } - false => { - Number::from(self.to_u1024_bytes().checked_shr(rhs as u32).unwrap_or(u1024::ZERO)) - } - }; - n.reshape(layout); - n - } -} - -impl Number { - /// Cyclic bit shift left. Panics if the number is not an integer. - pub fn scl(self, shift: Number) -> Number { - let layout = self.layout(); - let bits = self.len() * 8; - let lhs = self.into_unsigned(); - assert!(layout.is_integer(), "bit shifting float number"); - let excess = u16::from(shift) % bits; - let residue = lhs >> Number::from(bits - excess); - ((lhs << Number::from(excess)) | residue).reshaped(layout, true).expect("restoring layout") - } - - /// Cyclic bit shift right. Panics if the number is not an integer. - pub fn scr(self, shift: Number) -> Number { - let layout = self.layout(); - let bits = self.len() * 8; - let lhs = self.into_unsigned(); - assert!(layout.is_integer(), "bit shifting float number"); - let excess = u16::from(shift) % bits; - let residue = lhs << Number::from(bits - excess); - ((lhs >> Number::from(excess)) | residue).reshaped(layout, true).expect("restoring layout") - } - - /// Reverses the order of bits in the integer. The least significant bit becomes the most - /// significant bit, second least-significant bit becomes second most-significant bit, etc. - pub fn reverse_bits(mut self) -> Number { - assert!(self.layout().is_integer(), "reversing bit order of float"); - let bytes = &mut self[..]; - bytes.reverse(); - bytes.iter_mut().for_each(|byte| *byte = byte.reverse_bits()); - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn shl_test() { - let x = Number::from(6u8); - let y = Number::from(24u8); - assert_eq!(x.shl(Number::from(2)), y); - let x = Number::from(-1i16); - let y = Number::from(-2i16); - assert_eq!(x.shl(Number::from(1)), y); - } - - #[test] - fn shr_test() { - let x = Number::from(9u8); - let y = Number::from(4u8); - assert_eq!(x.shr(Number::from(1)), y); - let x = Number::from(-2i16); - let y = Number::from(-1i16); - assert_eq!(x.shr(Number::from(1)), y); - } - - #[test] - fn scl_test() { - let x = Number::from(131u8); - let y = Number::from(7u8); - assert_eq!(x.scl(Number::from(1)), y); - let x = Number::from(-7i16); - let y = Number::from(-25i16); - assert_eq!(x.scl(Number::from(2)), y); - } - - #[test] - fn scr_test() { - let x = Number::from(129u8); - let y = Number::from(192u8); - assert_eq!(x.scr(Number::from(1)), y); - let x = Number::from(1i8); - let y = Number::from(64i8); - assert_eq!(x.scr(Number::from(2)), y); - } - - #[test] - fn reverse_bits_test() { - let x = Number::from(192u8); - let y = Number::from(3u8); - assert_eq!(x.reverse_bits(), y); - let x = Number::from(1i8); - let y = Number::from(-128i8); - assert_eq!(x.reverse_bits(), y); - } -} diff --git a/src/data/byte_str.rs b/src/data/byte_str.rs deleted file mode 100644 index 0dcd9bc..0000000 --- a/src/data/byte_str.rs +++ /dev/null @@ -1,334 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::borrow::{Borrow, BorrowMut}; -use core::convert::TryFrom; -use core::fmt::{self, Debug, Display, Formatter}; -use core::ops::Range; - -use amplify::confinement::{SmallBlob, TinyBlob}; -use amplify::num::error::OverflowError; - -/// Large binary bytestring object. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ByteStr { - /// Adjusted slice length. - len: u16, - - /// Slice bytes - #[doc(hidden)] - pub bytes: Box<[u8; u16::MAX as usize]>, -} - -impl Default for ByteStr { - fn default() -> ByteStr { - ByteStr { - len: 0, - bytes: Box::new([0u8; u16::MAX as usize]), - } - } -} - -impl AsRef<[u8]> for ByteStr { - #[inline] - fn as_ref(&self) -> &[u8] { &self.bytes[..self.len as usize] } -} - -impl AsMut<[u8]> for ByteStr { - #[inline] - fn as_mut(&mut self) -> &mut [u8] { &mut self.bytes[..self.len as usize] } -} - -impl Borrow<[u8]> for ByteStr { - #[inline] - fn borrow(&self) -> &[u8] { &self.bytes[..self.len as usize] } -} - -impl BorrowMut<[u8]> for ByteStr { - #[inline] - fn borrow_mut(&mut self) -> &mut [u8] { &mut self.bytes[..self.len as usize] } -} - -impl Extend for ByteStr { - fn extend>(&mut self, iter: T) { - let mut pos = self.len(); - let iter = iter.into_iter(); - for byte in iter { - assert!(pos < u16::MAX); - self.bytes[pos as usize] = byte; - pos += 1; - } - self.len = pos; - } -} - -impl From<&TinyBlob> for ByteStr { - fn from(blob: &TinyBlob) -> Self { - let len = blob.len_u8() as u16; - let mut bytes = [0u8; u16::MAX as usize]; - bytes[0..(len as usize)].copy_from_slice(blob.as_slice()); - ByteStr { - len, - bytes: Box::new(bytes), - } - } -} - -impl From<&SmallBlob> for ByteStr { - fn from(blob: &SmallBlob) -> Self { - let len = blob.len_u16(); - let mut bytes = [0u8; u16::MAX as usize]; - bytes[0..(len as usize)].copy_from_slice(blob.as_slice()); - ByteStr { - len, - bytes: Box::new(bytes), - } - } -} - -impl From for ByteStr { - fn from(blob: TinyBlob) -> Self { ByteStr::from(&blob) } -} - -impl From for ByteStr { - fn from(blob: SmallBlob) -> Self { ByteStr::from(&blob) } -} - -impl TryFrom<&[u8]> for ByteStr { - type Error = OverflowError; - - fn try_from(slice: &[u8]) -> Result { - let len = slice.len(); - if len > u16::MAX as usize { - return Err(OverflowError { - max: u16::MAX as usize + 1, - value: len, - }); - } - let mut bytes = [0u8; u16::MAX as usize]; - bytes[0..len].copy_from_slice(slice.as_ref()); - Ok(ByteStr { - len: len as u16, - bytes: Box::new(bytes), - }) - } -} - -impl ByteStr { - /// Constructs blob from slice of bytes. - /// - /// Panics if the length of the slice is greater than `u16::MAX` bytes. - #[inline] - pub fn with(slice: impl AsRef<[u8]>) -> ByteStr { - ByteStr::try_from(slice.as_ref()) - .expect("internal error: ByteStr::with requires slice <= u16::MAX + 1") - } - - /// Returns correct length of the string, in range `0 ..= u16::MAX` - #[inline] - pub fn len(&self) -> u16 { self.len } - - /// Returns when the string has a zero length - #[inline] - pub fn is_empty(&self) -> bool { self.len == 0 } - - /// Adjusts the length of the string - #[inline] - pub fn adjust_len(&mut self, new_len: u16) { self.len = new_len } - - /// Extends the length of the string if necessary - #[inline] - pub fn extend_len(&mut self, new_len: u16) { self.len = new_len.max(self.len) } - - /// Fills range within a string with the provided byte value, increasing string length if - /// necessary - pub fn fill(&mut self, range: Range, val: u8) { - let start = range.start; - let end = range.end; - self.extend_len(end); - self.bytes[start as usize..end as usize].fill(val); - } - - /// Returns vector representation of the contained bytecode - #[inline] - pub fn to_vec(&self) -> Vec { self.as_ref().to_vec() } -} - -#[cfg(not(feature = "std"))] -impl Debug for ByteStr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{:#04X?}", self.as_ref()) } -} - -#[cfg(feature = "std")] -impl Debug for ByteStr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use amplify::hex::ToHex; - - f.debug_tuple("ByteStr").field(&self.as_ref().to_hex()).finish() - } -} - -#[cfg(feature = "std")] -impl Display for ByteStr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use std::fmt::Write; - - use amplify::hex::ToHex; - - let vec = Vec::from(&self.bytes[..self.len as usize]); - if f.alternate() { - for (line, slice) in self.as_ref().chunks(16).enumerate() { - write!(f, "\x1B[0;35m{:>1$x}0 | \x1B[0m", line, f.width().unwrap_or(1) - 1)?; - for (pos, byte) in slice.iter().enumerate() { - write!(f, "{:02x} ", byte)?; - if pos == 7 { - f.write_char(' ')?; - } - } - if slice.len() < 8 { - f.write_char(' ')?; - } - write!( - f, - "{:1$}\x1B[0;35m|\x1B[0m ", - ' ', - 16usize.saturating_sub(slice.len()) * 3 + 1 - )?; - for byte in slice { - f.write_str(&if byte.is_ascii_control() - || byte.is_ascii_whitespace() - || !byte.is_ascii() - { - s!("\x1B[0;35m·\x1B[0m") - } else { - String::from(char::from(*byte)) - })?; - } - f.write_char('\n')?; - } - Ok(()) - // write!(f, "{}..{}", self.bytes[..4].to_hex(), self.bytes[(self.len() - - // 4)..].to_hex()) - } else if let Ok(s) = String::from_utf8(vec) { - f.write_str("\"")?; - let mut ctl = false; - for c in s.chars() { - let v = c as u32; - if (c.is_control() && v != 0x20) || !(0x20..0x7F).contains(&v) { - if !ctl { - 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 { - ctl = false; - } - f.write_char(c)?; - } - } - f.write_str("\"") - } else { - f.write_str(&self.as_ref().to_hex()) - } - } -} - -#[cfg(not(feature = "std"))] -impl Display for ByteStr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{:#04X?}", self.as_ref()) } -} - -/* -#[cfg(feature = "strict_encoding")] -mod _strict_encoding { - use std::convert::TryFrom; - use std::io::{Read, Write}; - use std::ops::Deref; - - use strict_encoding::{StrictDecode, StrictEncode}; - - use super::ByteStr; - - impl StrictEncode for ByteStr { - fn strict_encode(&self, e: E) -> Result { - self.as_ref().strict_encode(e) - } - } - - impl StrictDecode for ByteStr { - fn strict_decode(d: D) -> Result { - let data = Vec::::strict_decode(d)?; - Ok(ByteStr::try_from(data.deref()).expect("strict encoding can't read more than 67 kb")) - } - } -} - */ - -#[cfg(feature = "serde")] -mod _serde { - use std::convert::TryFrom; - use std::ops::Deref; - - use amplify::hex::{FromHex, ToHex}; - use serde_crate::de::Error; - use serde_crate::{Deserialize, Deserializer, Serialize, Serializer}; - - use super::ByteStr; - - impl Serialize for ByteStr { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - if serializer.is_human_readable() { - self.as_ref().to_hex().serialize(serializer) - } else { - self.as_ref().serialize(serializer) - } - } - } - - impl<'de> Deserialize<'de> for ByteStr { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - let vec = if deserializer.is_human_readable() { - let hex = String::deserialize(deserializer)?; - Vec::::from_hex(&hex).map_err(D::Error::custom)? - } else { - Vec::::deserialize(deserializer)? - }; - ByteStr::try_from(vec.deref()) - .map_err(|_| D::Error::invalid_length(vec.len(), &"max u16::MAX bytes")) - } - } -} diff --git a/src/data/encoding.rs b/src/data/encoding.rs deleted file mode 100644 index f39f959..0000000 --- a/src/data/encoding.rs +++ /dev/null @@ -1,607 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -//! Helper traits and default implementations for encoding elements of AliVM container types - -use std::io::{self, Read, Write}; -use std::iter::FromIterator; -use std::marker::PhantomData; -use std::string::FromUtf8Error; - -use amplify::{confinement, IoError, Wrapper}; - -use crate::data::encoding::DecodeError::InvalidBool; -use crate::data::{ByteStr, FloatLayout, IntLayout, Layout, MaybeNumber, Number, NumberLayout}; -use crate::library::{IsaSegError, LibId, LibSite, SegmentError}; - -/// Trait for encodable container data structures used by AluVM and runtime environments -pub trait Encode { - /// Type-specific encoding error enumeration - type Error: std::error::Error + From; - - /// Encodes data structure to a writer - fn encode(&self, writer: impl Write) -> Result; - - /// Serializes data structure as an in-memory array - #[inline] - fn serialize(&self) -> Vec { - let mut wrter = vec![]; - self.encode(&mut wrter).expect("in-memory encoding"); - wrter - } -} - -/// Trait for container data structures which can be read or deserialized -pub trait Decode { - /// Type-specific decoding error enumeration - type Error: std::error::Error + From; - - /// Decodes data structure from a reader - fn decode(reader: impl Read) -> Result - where Self: Sized; - - /// Deserializes data structure from given byte slice - #[inline] - fn deserialize(from: impl AsRef<[u8]>) -> Result - where Self: Sized { - Self::decode(from.as_ref()) - } -} - -/// Errors encoding AluVM data containers -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum EncodeError { - /// data writing error ({0}) - #[from] - #[from(io::Error)] - Io(IoError), - - /// string length is {0}, which exceeds 255 bytes limit - StringTooLong(usize), - - /// collection contains {0} items, which exceeds [`u16::MAX`] limit - ByteLimitExceeded(usize), - - /// collection contains {0} items, which exceeds [`u16::MAX`] limit - WordLimitExceeded(usize), -} - -/// Errors decoding AluVM data containers -#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum DecodeError { - /// data reading error ({0}) - #[from] - #[from(io::Error)] - Io(IoError), - - /// invalid bool value `{0}` - InvalidBool(u8), - - /// invalid UTF8 string data - /// - /// details: {0} - #[from] - InvalidUtf8(FromUtf8Error), - - /// number data does not match provided layout {0:?} - /// - /// number data: {1:#02x?} - NumberLayout(Layout, Vec), - - /// unknown float layout type `{0}` - FloatLayout(u8), - - /// Library construction errors - #[display(inner)] - #[from] - Lib(SegmentError), - - /// Library segment construction error - #[display(inner)] - #[from] - LibSeg(confinement::Error), - - /// ISAE segment construction error - #[display(inner)] - #[from] - IsaSeg(IsaSegError), -} - -/// Wrapper around collections which may contain at most [`u8::MAX`] elements -pub struct MaxLenByte(pub I, PhantomData); - -impl MaxLenByte { - /// Constructs limited-size wrapper around the provided type - pub fn new(iter: I) -> Self { Self(iter, Default::default()) } - - /// Releases inner type - pub fn release(self) -> I { self.0 } -} - -/// Wrapper around collections which may contain at most [`u16::MAX`] elements -pub struct MaxLenWord(pub I, PhantomData); - -impl MaxLenWord { - /// Constructs limited-size wrapper around the provided type - pub fn new(iter: I) -> Self { Self(iter, Default::default()) } - - /// Releases inner type - pub fn release(self) -> I { self.0 } -} - -impl Encode for &T -where T: Encode -{ - type Error = T::Error; - - #[inline] - fn encode(&self, writer: impl Write) -> Result { (*self).encode(writer) } -} - -impl Encode for bool { - type Error = io::Error; - - #[inline] - fn encode(&self, writer: impl Write) -> Result { - (*self as u8).encode(writer) - } -} - -impl Decode for bool { - type Error = DecodeError; - - #[inline] - fn decode(reader: impl Read) -> Result - where Self: Sized { - match u8::decode(reader)? { - 0 => Ok(false), - 1 => Ok(true), - invalid => Err(InvalidBool(invalid)), - } - } -} - -impl Encode for u8 { - type Error = io::Error; - - #[inline] - fn encode(&self, mut writer: impl Write) -> Result { - writer.write_all(&[*self])?; - Ok(1) - } -} - -impl Decode for u8 { - type Error = io::Error; - - #[inline] - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let mut byte = [0u8; 1]; - reader.read_exact(&mut byte)?; - Ok(byte[0]) - } -} - -impl Encode for u16 { - type Error = io::Error; - - #[inline] - fn encode(&self, mut writer: impl Write) -> Result { - writer.write_all(&self.to_le_bytes())?; - Ok(2) - } -} - -impl Decode for u16 { - type Error = io::Error; - - #[inline] - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let mut word = [0u8; 2]; - reader.read_exact(&mut word)?; - Ok(u16::from_le_bytes(word)) - } -} - -impl Encode for String { - type Error = EncodeError; - - fn encode(&self, mut writer: impl Write) -> Result { - let len = self.as_bytes().len(); - if len > u8::MAX as usize { - return Err(EncodeError::StringTooLong(len)); - } - (len as u8).encode(&mut writer)?; - writer.write_all(self.as_bytes())?; - Ok(len + 1) - } -} - -impl Decode for String { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let len = u8::decode(&mut reader)?; - let mut s = vec![0u8; len as usize]; - reader.read_exact(&mut s)?; - String::from_utf8(s).map_err(DecodeError::from) - } -} - -impl Encode for Option { - type Error = EncodeError; - - #[inline] - fn encode(&self, writer: impl Write) -> Result { - if let Some(s) = self { - s.encode(writer) - } else { - Ok(0u8.encode(writer)?) - } - } -} - -impl Decode for Option { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let len = u8::decode(&mut reader)?; - if len == 0 { - return Ok(None); - } - let mut s = vec![0u8; len as usize]; - reader.read_exact(&mut s)?; - String::from_utf8(s).map_err(DecodeError::from).map(Some) - } -} - -impl<'i, I> Encode for MaxLenByte<&'i I> -where - &'i I: IntoIterator, - <&'i I as IntoIterator>::IntoIter: ExactSizeIterator, - <&'i I as IntoIterator>::Item: Encode, - EncodeError: From<<<&'i I as IntoIterator>::Item as Encode>::Error>, -{ - type Error = EncodeError; - - fn encode(&self, mut writer: impl Write) -> Result { - let iter = self.0.into_iter(); - let len = iter.len(); - if len > u8::MAX as usize { - return Err(EncodeError::ByteLimitExceeded(len)); - } - (len as u8).encode(&mut writer)?; - let mut count = 1; - for item in iter { - count += item.encode(&mut writer)?; - } - Ok(count) - } -} - -impl Decode for MaxLenByte -where - T: FromIterator, - I: Decode, - DecodeError: From<::Error>, -{ - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let len = u8::decode(&mut reader)?; - let mut vec = vec![]; - for _ in 0..len { - vec.push(I::decode(&mut reader)?); - } - Ok(MaxLenByte::new(vec.into_iter().collect())) - } -} - -impl<'i, I> Encode for MaxLenWord<&'i I> -where - &'i I: IntoIterator, - <&'i I as IntoIterator>::IntoIter: ExactSizeIterator, - <&'i I as IntoIterator>::Item: Encode, - EncodeError: From<<<&'i I as IntoIterator>::Item as Encode>::Error>, -{ - type Error = EncodeError; - - fn encode(&self, mut writer: impl Write) -> Result { - let iter = self.0.into_iter(); - let len = iter.len(); - if len > u16::MAX as usize { - return Err(EncodeError::WordLimitExceeded(len)); - } - (len as u16).encode(&mut writer)?; - let mut count = 2; - for item in iter { - count += item.encode(&mut writer)?; - } - Ok(count) - } -} - -impl Decode for MaxLenWord -where - T: FromIterator, - I: Decode, - DecodeError: From<::Error>, -{ - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let len = u16::decode(&mut reader)?; - let mut vec = vec![]; - for _ in 0..len { - vec.push(I::decode(&mut reader)?); - } - Ok(MaxLenWord::new(vec.into_iter().collect())) - } -} - -impl Encode for (A, B) -where - A: Encode, - B: Encode, - EncodeError: From + From, -{ - type Error = EncodeError; - - #[inline] - fn encode(&self, mut writer: impl Write) -> Result { - Ok(self.0.encode(&mut writer)? + self.1.encode(&mut writer)?) - } -} - -impl Decode for (A, B) -where - A: Decode, - B: Decode, - DecodeError: From + From, -{ - type Error = DecodeError; - - #[inline] - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - Ok((A::decode(&mut reader)?, B::decode(&mut reader)?)) - } -} - -impl Encode for ByteStr { - type Error = io::Error; - - fn encode(&self, mut writer: impl Write) -> Result { - let len = self.len(); - len.encode(&mut writer)?; - writer.write_all(self.as_ref())?; - Ok(len as usize + 2) - } -} - -impl Decode for ByteStr { - type Error = io::Error; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let len = u16::decode(&mut reader)?; - let mut vec = vec![0u8; len as usize]; - reader.read_exact(&mut vec)?; - Ok(ByteStr::with(vec)) - } -} - -impl Encode for Option { - type Error = io::Error; - - #[inline] - fn encode(&self, writer: impl Write) -> Result { - if let Some(s) = self { - s.encode(writer) - } else { - Ok(0u8.encode(writer)?) - } - } -} - -impl Decode for Option { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let len = u8::decode(&mut reader)?; - if len == 0 { - return Ok(None); - } - let mut s = vec![0u8; len as usize]; - reader.read_exact(&mut s)?; - Ok(Some(ByteStr::with(s))) - } -} - -impl Encode for Number { - type Error = io::Error; - - #[inline] - fn encode(&self, mut writer: impl Write) -> Result { - let count = self.layout().encode(&mut writer)?; - writer.write_all(self.as_ref())?; - - let len = self.len(); - Ok(count + len as usize) - } -} - -impl Decode for Number { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let layout = Layout::decode(&mut reader)?; - let mut vec = vec![0u8; layout.bytes() as usize]; - reader.read_exact(&mut vec)?; - Number::with(&vec, layout).ok_or(DecodeError::NumberLayout(layout, vec)) - } -} - -impl Encode for MaybeNumber { - type Error = io::Error; - - fn encode(&self, mut writer: impl Write) -> Result { - match **self { - Some(number) => Ok(1u16.encode(&mut writer)? + number.encode(&mut writer)?), - None => 0u16.encode(writer), - } - } -} - -impl Decode for MaybeNumber { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - match u8::decode(&mut reader)? { - 0 => Ok(MaybeNumber::none()), - 1 => Ok(Number::decode(reader)?.into()), - unknown => Err(DecodeError::InvalidBool(unknown)), - } - } -} - -impl Encode for IntLayout { - type Error = io::Error; - - #[inline] - fn encode(&self, mut writer: impl Write) -> Result { - Ok(self.signed.encode(&mut writer)? + self.bytes.encode(&mut writer)?) - } -} - -impl Decode for IntLayout { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - Ok(IntLayout { - signed: bool::decode(&mut reader)?, - bytes: u16::decode(&mut reader)?, - }) - } -} - -impl Encode for FloatLayout { - type Error = io::Error; - - #[inline] - fn encode(&self, writer: impl Write) -> Result { - (*self as u8).encode(writer) - } -} - -impl Decode for FloatLayout { - type Error = DecodeError; - - fn decode(reader: impl Read) -> Result - where Self: Sized { - let val = u8::decode(reader)?; - FloatLayout::with(val).ok_or(DecodeError::FloatLayout(val)) - } -} - -impl Encode for Layout { - type Error = io::Error; - - #[inline] - fn encode(&self, writer: impl Write) -> Result { - match self { - Layout::Integer(layout) => layout.encode(writer), - Layout::Float(layout) => layout.encode(writer), - } - } -} - -impl Decode for Layout { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - Ok(match u8::decode(&mut reader)? { - i if i <= 1 => IntLayout { - signed: i == 1, - bytes: u16::decode(reader)?, - } - .into(), - float => FloatLayout::with(float).ok_or(DecodeError::FloatLayout(float))?.into(), - }) - } -} - -impl Encode for LibId { - type Error = io::Error; - - fn encode(&self, mut writer: impl Write) -> Result { - let slice = self.as_slice(); - writer.write_all(slice)?; - Ok(slice.len()) - } -} - -impl Decode for LibId { - type Error = io::Error; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let mut slice = [0u8; 32]; - reader.read_exact(&mut slice)?; - Ok(LibId::from_inner(slice.into())) - } -} - -impl Encode for LibSite { - type Error = io::Error; - - fn encode(&self, mut writer: impl Write) -> Result { - Ok(self.lib.encode(&mut writer)? + self.pos.encode(&mut writer)?) - } -} - -impl Decode for LibSite { - type Error = io::Error; - - fn decode(mut reader: impl Read) -> Result - where Self: Sized { - let id = LibId::decode(&mut reader)?; - let pos = u16::decode(&mut reader)?; - Ok(LibSite::with(pos, id)) - } -} diff --git a/src/data/mod.rs b/src/data/mod.rs deleted file mode 100644 index 79d8e89..0000000 --- a/src/data/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -//! Internal data representations and operations on data used by AluVM - -mod arithm; -mod bitwise; -mod byte_str; -#[cfg(feature = "std")] -pub mod encoding; -mod number; - -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 deleted file mode 100644 index 6f4f565..0000000 --- a/src/data/number.rs +++ /dev/null @@ -1,1784 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -//! Module defining number layout (integer, signed/unsigned, float etc) and universal in-memory -//! number representation. - -#[cfg(feature = "alloc")] -use alloc::format; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -use core::fmt::{ - self, Debug, Display, Formatter, LowerExp, LowerHex, Octal, UpperExp, UpperHex, Write, -}; -use core::hash::{Hash, Hasher}; -use core::ops::{ - Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, -}; -use core::str::FromStr; - -use amplify::num::apfloat::{ieee, Float, Status, StatusAnd}; -use amplify::num::{i1024, i256, i512, u1024, u256, u512}; -use half::bf16; - -/// Trait of different number layouts -pub trait NumberLayout: Copy { - /// Returns how many bits are used by the layout - #[inline] - fn bits(self) -> u16 { self.bytes() * 8 } - - /// Returns how many bytes are used by the layout - fn bytes(self) -> u16; - - /// Returns whether layout supports signed numbers - fn is_signed(self) -> bool; - - /// Detects whether layout uses fixed number of bits or may be applied to the numbers with - /// arbitrary bit size. - #[inline] - fn is_fixed_width(self) -> bool { true } - - /// Returns bit number which keeps (or may be used to store) sign information - fn sign_bit(self) -> u16; - - /// Returns byte number which keeps (or may be used to store) sign information - fn sign_byte(self) -> u16; -} - -/// Layout of the value encoding. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[display(inner)] -pub enum Layout { - /// Integer layout - Integer(IntLayout), - - /// Float layouts - Float(FloatLayout), -} - -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 const fn signed(bytes: u16) -> Layout { Layout::Integer(IntLayout::signed(bytes)) } - - /// Returns unsigned integer layout - #[inline] - pub const fn unsigned(bytes: u16) -> Layout { Layout::Integer(IntLayout::unsigned(bytes)) } - - /// Constructs float layout - #[inline] - pub const fn float(layout: FloatLayout) -> Layout { Layout::Float(layout) } - - /// Detects if the number layout is unsigned integer - #[inline] - 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 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 const fn is_integer(self) -> bool { matches!(self, Layout::Integer(_)) } - - /// Detects if the number layout is one of float layouts - #[inline] - pub const fn is_float(self) -> bool { matches!(self, Layout::Float(_)) } - - /// Converts unsigned integer layout into signed; does nothing for float layouts - #[inline] - pub fn into_signed(mut self) -> Layout { - if let Layout::Integer(il) = &mut self { - *il = il.into_signed() - } - self - } - - /// Converts signed integer layout into unsigned; does nothing for float layouts - #[inline] - pub fn into_unsigned(mut self) -> Layout { - if let Layout::Integer(il) = &mut self { - *il = il.into_unsigned() - } - self - } - - /// Updates integer layout (if used) to match signed/unsigned format of some other layout. - /// Does nothing if any of the layouts are not integer layouts or `other` layout is unsigned. - #[inline] - pub fn using_sign(mut self, other: Layout) -> Layout { - if let (Layout::Integer(il), Layout::Integer(il2)) = (&mut self, other) { - *il = il2.using_sign(il2) - } - self - } -} - -impl NumberLayout for Layout { - #[inline] - fn bytes(self) -> u16 { - match self { - Layout::Integer(il) => il.bytes(), - Layout::Float(fl) => fl.bytes(), - } - } - - #[inline] - fn is_signed(self) -> bool { matches!(self, Layout::Integer(IntLayout { signed: true, .. })) } - - #[inline] - fn sign_bit(self) -> u16 { - match self { - Layout::Integer(il) => il.sign_bit(), - Layout::Float(fl) => fl.sign_bit(), - } - } - - #[inline] - fn sign_byte(self) -> u16 { - match self { - Layout::Integer(il) => il.sign_byte(), - Layout::Float(fl) => fl.sign_byte(), - } - } -} - -/// Layout of the integer value encoding. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct IntLayout { - /// Format of the integer (signed or unsigned). - /// - /// Unsigned integer: exact correspondence of bits to bytes in little-endian bit format - /// - /// Signed integer: the most significant bit (highest bit) indicates value sign. For the - /// negative numbers the value is modulo-divided by the maximum number. - pub signed: bool, - - /// Number of bytes occupied by the number - pub bytes: u16, -} - -impl Display for IntLayout { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_char(if self.signed { 'i' } else { 'u' })?; - write!(f, "{}", self.bits()) - } -} - -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 const fn signed(bytes: u16) -> IntLayout { - Self { - signed: true, - bytes, - } - } - - /// Returns unsigned integer layout - #[inline] - pub const fn unsigned(bytes: u16) -> IntLayout { - Self { - signed: false, - bytes, - } - } - - /// Converts unsigned integer layout into signed - #[inline] - pub const fn into_signed(mut self) -> IntLayout { - self.signed = true; - self - } - - /// Converts signed integer layout into unsigned - #[inline] - 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 const fn using_sign(mut self, other: IntLayout) -> IntLayout { - self.signed = other.signed; - self - } - - /// Returns whether a `usize`-value fits the layout dimensions. - pub fn fits_usize(self, value: usize) -> bool { - self.bits() >= (usize::BITS - value.leading_zeros()) as u16 - } -} - -impl NumberLayout for IntLayout { - #[inline] - fn bytes(self) -> u16 { self.bytes } - - #[inline] - fn is_signed(self) -> bool { self.signed } - - #[inline] - fn sign_bit(self) -> u16 { self.bits() - 1 } - - #[inline] - fn sign_byte(self) -> u16 { self.bytes() - 1 } -} - -impl From for Layout { - #[inline] - fn from(layout: IntLayout) -> Self { Layout::Integer(layout) } -} - -impl From<&IntLayout> for Layout { - #[inline] - fn from(layout: &IntLayout) -> Self { Layout::Integer(*layout) } -} - -/// Layout of the float value encoding. -/// -/// Defines bit dimensionality and encoding format for float types. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum FloatLayout { - /// 16-bit bfloat16 format used in machine learning - #[display("bfloat16")] - BFloat16 = 2, - - /// 16-bit IEEE-754 binary16 half-precision - #[display("ieee:binary16")] - IeeeHalf = 3, - - /// 32-bit IEEE-754 binary32 single-precision - #[display("ieee:binary32")] - IeeeSingle = 4, - - /// 64-bit IEEE-754 binary64 double-precision - #[display("ieee:binary64")] - IeeeDouble = 5, - - /// 80-bit IEEE-754 extended precision - #[display("x87:binary80")] - X87DoubleExt = 6, - - /// 128-bit IEEE-754 binary128 quadruple precision - #[display("ieee:binary128")] - IeeeQuad = 7, - - /// 256-bit IEEE-754 binary256 octuple precision - #[display("ieee:binary256")] - IeeeOct = 8, - - /// 512-bit tapered floating point - #[display("tapered:binary512")] - FloatTapered = 9, -} - -impl NumberLayout for FloatLayout { - fn bytes(self) -> u16 { - match self { - FloatLayout::BFloat16 => 2, - FloatLayout::IeeeHalf => 2, - FloatLayout::IeeeSingle => 4, - FloatLayout::IeeeDouble => 8, - FloatLayout::X87DoubleExt => 10, - FloatLayout::IeeeQuad => 16, - FloatLayout::IeeeOct => 32, - FloatLayout::FloatTapered => 64, - } - } - - #[inline] - fn is_signed(self) -> bool { true } - - #[inline] - fn sign_bit(self) -> u16 { self.bits() - 1 } - - #[inline] - fn sign_byte(self) -> u16 { self.bytes() - 1 } -} - -impl FloatLayout { - /// Constructs [`FloatLayout`] from byte representation - pub fn with(value: u8) -> Option { - Some(match value { - x if x == FloatLayout::BFloat16 as u8 => FloatLayout::BFloat16, - x if x == FloatLayout::IeeeHalf as u8 => FloatLayout::IeeeHalf, - x if x == FloatLayout::IeeeSingle as u8 => FloatLayout::IeeeSingle, - x if x == FloatLayout::IeeeDouble as u8 => FloatLayout::IeeeDouble, - x if x == FloatLayout::IeeeQuad as u8 => FloatLayout::IeeeQuad, - x if x == FloatLayout::IeeeOct as u8 => FloatLayout::IeeeOct, - x if x == FloatLayout::X87DoubleExt as u8 => FloatLayout::X87DoubleExt, - x if x == FloatLayout::FloatTapered as u8 => FloatLayout::FloatTapered, - _ => return None, - }) - } - - /// Detects if layout is used for encoding floating-point numbers - #[inline] - pub fn is_float(self) -> bool { self as u8 > 1 } - - /// Detects if this layout uses variable significand/exponent size - #[inline] - pub fn is_tapered(self) -> bool { self == FloatLayout::FloatTapered } - - /// For float numbers returns range of bits used by significand. For integer numbers always - /// `None`. - #[inline] - pub fn significand_pos(self) -> Option> { - match self { - FloatLayout::BFloat16 => Some(0..7), - FloatLayout::IeeeHalf => Some(0..10), - FloatLayout::IeeeSingle => Some(0..23), - FloatLayout::IeeeDouble => Some(0..52), - FloatLayout::X87DoubleExt => Some(0..64), - FloatLayout::IeeeQuad => Some(0..112), - FloatLayout::IeeeOct => Some(0..236), - FloatLayout::FloatTapered => None, - } - } - - /// For float numbers returns range of bits used by exponent. For integer numbers always `None`. - #[inline] - pub fn exponent_pos(self) -> Option> { - match self { - FloatLayout::BFloat16 => Some(7..15), - FloatLayout::IeeeHalf => Some(10..15), - FloatLayout::IeeeSingle => Some(23..31), - FloatLayout::IeeeDouble => Some(52..63), - FloatLayout::X87DoubleExt => Some(64..79), - FloatLayout::IeeeQuad => Some(112..127), - FloatLayout::IeeeOct => Some(236..255), - FloatLayout::FloatTapered => None, - } - } -} - -impl From for Layout { - #[inline] - fn from(layout: FloatLayout) -> Self { Layout::Float(layout) } -} - -impl From<&FloatLayout> for Layout { - #[inline] - fn from(layout: &FloatLayout) -> Self { Layout::Float(*layout) } -} - -/// Representation of the value from a register, which may be `None` if the register is unset. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, From)] -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 const fn none() -> MaybeNumber { MaybeNumber(None) } - - /// Creates [`MaybeNumber`] assigning a value to it - #[inline] - 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 - #[inline] - pub fn reshape(&mut self, to: Layout) -> bool { - match self.0 { - None => true, - Some(ref mut val) => val.reshape(to), - } - } -} - -impl From for MaybeNumber { - fn from(val: Number) -> Self { MaybeNumber(Some(val)) } -} - -impl From<&Number> for MaybeNumber { - fn from(val: &Number) -> Self { MaybeNumber(Some(*val)) } -} - -impl From<&Option> for MaybeNumber { - fn from(val: &Option) -> Self { MaybeNumber(*val) } -} - -impl From> for MaybeNumber { - fn from(val: Option<&Number>) -> Self { MaybeNumber(val.copied()) } -} - -impl From for Option { - fn from(val: MaybeNumber) -> Self { val.0 } -} - -impl Deref for MaybeNumber { - type Target = Option; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl Display for MaybeNumber { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.0 { - None => f.write_str("~"), - Some(ref val) => Display::fmt(val, f), - } - } -} - -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; - - fn from_str(s: &str) -> Result { - Ok(if s.contains('p') || s.contains('.') { - ieee::Quad::from_str(s)?.into() - } else { - Number::from_str(s)?.into() - }) - } -} - -/// Type holding number of any layout -#[derive(Copy, Clone)] -pub struct Number { - /// Internal number representation, up to the possible maximum size of any supported number - /// layout - bytes: [u8; 1024], - - /// Number layout used by the value - layout: Layout, -} - -impl Hash for Number { - fn hash(&self, state: &mut H) { - let clean = self.to_clean(); - clean.layout.hash(state); - state.write(&clean.bytes); - } -} - -impl Default for Number { - fn default() -> Number { - Number { - layout: Layout::Integer(IntLayout::unsigned(1)), - bytes: [0u8; 1024], - } - } -} - -impl AsRef<[u8]> for Number { - fn as_ref(&self) -> &[u8] { &self[..] } -} - -impl AsMut<[u8]> for Number { - fn as_mut(&mut self) -> &mut [u8] { &mut self[..] } -} - -impl Index for Number { - type Output = u8; - - fn index(&self, index: u16) -> &Self::Output { - assert!(index < self.len()); - &self.bytes[index as usize] - } -} - -impl IndexMut for Number { - fn index_mut(&mut self, index: u16) -> &mut Self::Output { - assert!(index < self.len()); - &mut self.bytes[index as usize] - } -} - -impl Index for Number { - type Output = [u8]; - - fn index(&self, _: RangeFull) -> &Self::Output { &self.bytes[..self.len() as usize] } -} - -impl IndexMut for Number { - fn index_mut(&mut self, _: RangeFull) -> &mut Self::Output { - let len = self.len() as usize; - &mut self.bytes[..len] - } -} - -impl Index> for Number { - type Output = [u8]; - - fn index(&self, index: Range) -> &Self::Output { - assert!(index.start < self.len() && index.end <= self.len()); - &self.bytes[index.start as usize..index.end as usize] - } -} - -impl IndexMut> for Number { - fn index_mut(&mut self, index: Range) -> &mut Self::Output { - assert!(index.start < self.len() && index.end <= self.len()); - &mut self.bytes[index.start as usize..index.end as usize] - } -} - -impl Index> for Number { - type Output = [u8]; - - fn index(&self, index: RangeInclusive) -> &Self::Output { - assert!(*index.start() < self.len() && *index.end() < self.len()); - &self.bytes[*index.start() as usize..*index.end() as usize] - } -} - -impl IndexMut> for Number { - fn index_mut(&mut self, index: RangeInclusive) -> &mut Self::Output { - &mut self.bytes[*index.start() as usize..*index.end() as usize] - } -} - -impl Index> for Number { - type Output = [u8]; - - fn index(&self, index: RangeFrom) -> &Self::Output { - assert!(index.start < self.len()); - &self.bytes[index.start as usize..self.len() as usize] - } -} - -impl IndexMut> for Number { - fn index_mut(&mut self, index: RangeFrom) -> &mut Self::Output { - assert!(index.start < self.len()); - let len = self.len() as usize; - &mut self.bytes[index.start as usize..len] - } -} - -impl Index> for Number { - type Output = [u8]; - - fn index(&self, index: RangeTo) -> &Self::Output { - assert!(index.end <= self.len()); - &self.bytes[..index.end as usize] - } -} - -impl IndexMut> for Number { - fn index_mut(&mut self, index: RangeTo) -> &mut Self::Output { - assert!(index.end <= self.len()); - &mut self.bytes[..index.end as usize] - } -} - -impl Index> for Number { - type Output = [u8]; - - fn index(&self, index: RangeToInclusive) -> &Self::Output { - assert!(index.end < self.len()); - &self.bytes[..=index.end as usize] - } -} - -impl IndexMut> for Number { - fn index_mut(&mut self, index: RangeToInclusive) -> &mut Self::Output { - assert!(index.end < self.len()); - &mut self.bytes[..=index.end as usize] - } -} - -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 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] - pub fn masked_bit(bit_no: u16, layout: Layout) -> Number { - let mut zero = Number { - layout, - bytes: [0u8; 1024], - }; - zero.bytes[(bit_no / 8) as usize] = 1 << (bit_no % 8); - zero - } - - /// Constructs number representation from a slice and a given layout. - /// - /// Fails returning `None` if the length of slice does not match the required layout byte - /// length. - pub fn with(slice: impl AsRef<[u8]>, layout: impl Into) -> Option { - let layout = layout.into(); - let slice = slice.as_ref(); - if slice.len() != layout.bytes() as usize { - return None; - } - let mut me = Number::from_slice(slice); - me.layout = layout; - Some(me) - } - - /// Constructs value from slice of bytes. - /// - /// Panics if the length of the slice is greater than 1024 bytes. - pub fn from_slice(slice: impl AsRef<[u8]>) -> Number { - let len = slice.as_ref().len(); - let mut bytes = [0u8; 1024]; - bytes[0..len].copy_from_slice(slice.as_ref()); - Number { - layout: Layout::unsigned(len as u16), - bytes, - } - } - - /// Constructs value from hex string - #[cfg(feature = "std")] - pub fn from_hex(s: &str) -> Result { - use amplify::hex::FromHex; - let s = s.trim_start_matches("0x"); - let len = s.len() / 2; - if len > 1024 { - return Err(amplify::hex::Error::InvalidLength(1024, len)); - } - let mut bytes = [0u8; 1024]; - let hex = Vec::::from_hex(s)?; - bytes[0..len].copy_from_slice(&hex); - Ok(Number { - layout: Layout::unsigned(hex.len() as u16), - bytes, - }) - } - - /// Serializes value in hexadecimal format to a string - #[cfg(feature = "std")] - pub fn to_hex(self) -> String { - let mut ret = String::with_capacity(2usize * self.len() as usize + 2); - write!(ret, "0x").expect("writing to string"); - for ch in &self.bytes { - write!(ret, "{:02x}", ch).expect("writing to string"); - } - ret - } - - /// Returns length of the used portion of the value - #[inline] - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> u16 { self.layout.bytes() } - - /// Returns number layout used by the value - #[inline] - pub fn layout(&self) -> Layout { self.layout } - - /// Returns the number of zeros in the binary representation of `self`. - #[inline] - pub fn count_zeros(&self) -> u16 { self.len() - self.count_ones() } - - /// Returns the number of ones in the binary representation of `self`. - pub fn count_ones(&self) -> u16 { - let mut count = 0u16; - for byte in &self[..] { - count += byte.count_ones() as u16; - } - count - } - - /// Measures minimum number of bits required to store the number. For float layouts, always - /// matches the layout bit size. - pub fn min_bit_len(&self) -> u16 { - if self.layout.is_float() { - return self.layout.bits(); - } - if self.len() == 0 { - return 0; - } - let empty_bytes = self[..] - .iter() - .rev() - .take_while(|&&v| if self.is_negative() { v == 0xff } else { v == 0 }) - .count() as u16; - let index = if self.len() > empty_bytes { self.len() - empty_bytes - 1 } else { 0 }; - let head_bits = match self.is_negative() { - true => 8 - self[index].leading_ones(), - false => 8 - self[index].leading_zeros(), - }; - index * 8 + head_bits as u16 + self.layout.is_signed() as u16 - } - - /// Detects if the number value positive (i.e. `>0`) or not. - pub fn is_positive(self) -> bool { - if self.layout.is_unsigned_int() { - return true; - } - if self.is_zero() { - return false; - } - self[self.layout.sign_byte()] & 0x80 == 0 - } - - /// Detects if the number value negative (i.e. `<0`) or not. - pub fn is_negative(self) -> bool { !self.is_zero() && !self.is_positive() } - - /// Detects if the value is equal to zero - pub fn is_zero(self) -> bool { - let mut clean = self.to_clean(); - if self.layout.is_float() { - clean = clean.without_sign().expect("should not fail when it is float"); - } - clean.bytes == [0; 1024] - } - - /// Detects if the value is `NaN`. For integer layouts always false - pub fn is_nan(self) -> bool { - match self.layout { - Layout::Integer(_) => false, - Layout::Float(FloatLayout::BFloat16) => bf16::from(self).is_nan(), - Layout::Float(FloatLayout::IeeeHalf) => ieee::Half::from(self).is_nan(), - Layout::Float(FloatLayout::IeeeSingle) => ieee::Single::from(self).is_nan(), - Layout::Float(FloatLayout::IeeeDouble) => ieee::Double::from(self).is_nan(), - Layout::Float(FloatLayout::IeeeQuad) => ieee::Quad::from(self).is_nan(), - Layout::Float(FloatLayout::IeeeOct) => ieee::Oct::from(self).is_nan(), - Layout::Float(FloatLayout::X87DoubleExt) => { - ieee::X87DoubleExtended::from(self).is_nan() - } - Layout::Float(FloatLayout::FloatTapered) => todo!("(#5) tapered float NaN detection"), - } - } - - /// Detects if the value is equal to the maximum possible value for the used layout. For floats, - /// always `false`. - pub fn is_max(self) -> bool { - match self.layout { - Layout::Integer(int_layout) => { - let mut mask = u1024::from(0u8); - for _ in 0..int_layout.bytes - int_layout.is_signed() as u16 { - mask <<= 1; - mask |= 1u8; - } - self.to_clean() == mask.into() - } - _ => false, - } - } - - /// Ensures that all non-value bits are set to zero - #[inline] - pub fn clean(&mut self) { - let len = self.len() as usize; - self.bytes[len..].fill(0); - } - - /// Returns a copy where all non-value bits are set to zero - #[inline] - pub fn to_clean(mut self) -> Self { - self.clean(); - self - } - - /// Converts unsigned integer number into signed; does nothing for float numbers - #[inline] - pub fn into_signed(mut self) -> Number { - if let Layout::Integer(il) = &mut self.layout { - *il = il.into_signed() - } - self - } - - /// Converts signed integer number into unsigned; does nothing for float numbers - #[inline] - pub fn into_unsigned(mut self) -> Number { - if let Layout::Integer(il) = &mut self.layout { - *il = il.into_unsigned() - } - self - } - - /// Transforms internal value layout returning whether this was possible without discarding any - /// bit information - pub fn reshape(&mut self, to: Layout) -> bool { - match (self.layout, to) { - (from, to) if from == to => true, - ( - Layout::Integer(IntLayout { - signed: true, - bytes: b_from, - }), - Layout::Integer(IntLayout { - signed: true, - bytes: b_to, - }), - ) if !self.is_positive() && b_from < b_to => { - self.layout = to; - for i in b_from..b_to { - self[i] = 255u8; - } - self.clean(); - true - } - // We need to change only bit dimensions - (Layout::Integer(IntLayout { .. }), Layout::Integer(IntLayout { bytes: len2, .. })) => { - let bit_len = self.min_bit_len(); - self.layout = to; - self.clean(); - bit_len <= len2 * 8 - } - (Layout::Float(l1), Layout::Float(l2)) => { - let value = match l1 { - FloatLayout::BFloat16 => bf16::from(*self).to_string(), - FloatLayout::IeeeHalf => ieee::Half::from(*self).to_string(), - FloatLayout::IeeeSingle => ieee::Single::from(*self).to_string(), - FloatLayout::IeeeDouble => ieee::Double::from(*self).to_string(), - FloatLayout::X87DoubleExt => ieee::X87DoubleExtended::from(*self).to_string(), - FloatLayout::IeeeQuad => ieee::Quad::from(*self).to_string(), - FloatLayout::IeeeOct => { - unimplemented!("IEEE octal precision layout conversion") - } - FloatLayout::FloatTapered => unimplemented!("tapered float layout conversion"), - }; - *self = match l2 { - FloatLayout::BFloat16 => bf16::from_str(&value) - .ok() - .and_then(|v| MaybeNumber::from(v).0) - .expect("float layout conversion"), - FloatLayout::IeeeHalf => ieee::Half::from_str(&value) - .ok() - .and_then(|v| MaybeNumber::from(v).0) - .expect("float layout conversion"), - FloatLayout::IeeeSingle => ieee::Single::from_str(&value) - .ok() - .and_then(|v| MaybeNumber::from(v).0) - .expect("float layout conversion"), - FloatLayout::IeeeDouble => ieee::Double::from_str(&value) - .ok() - .and_then(|v| MaybeNumber::from(v).0) - .expect("float layout conversion"), - FloatLayout::X87DoubleExt => ieee::X87DoubleExtended::from_str(&value) - .ok() - .and_then(|v| MaybeNumber::from(v).0) - .expect("float layout conversion"), - FloatLayout::IeeeQuad => ieee::Quad::from_str(&value) - .ok() - .and_then(|v| MaybeNumber::from(v).0) - .expect("float layout conversion"), - FloatLayout::IeeeOct => { - unimplemented!("IEEE octal precision layout conversion") - } - FloatLayout::FloatTapered => unimplemented!("tapered float layout conversion"), - }; - false - } - (Layout::Float(fl), Layout::Integer(_)) => { - let val = match fl { - FloatLayout::BFloat16 => todo!("BFloat16 to integer conversion"), - FloatLayout::IeeeHalf => ieee::Half::from(*self).to_i256(256), - FloatLayout::IeeeSingle => ieee::Single::from(*self).to_i256(256), - FloatLayout::IeeeDouble => ieee::Double::from(*self).to_i256(256), - FloatLayout::X87DoubleExt => ieee::X87DoubleExtended::from(*self).to_i256(256), - FloatLayout::IeeeQuad => ieee::Quad::from(*self).to_i256(256), - FloatLayout::IeeeOct => ieee::Oct::from(*self).to_i256(256), - FloatLayout::FloatTapered => unimplemented!("tapered float layout conversion"), - }; - *self = Number::from(val.value); - self.reshape(to); - val.status == Status::OK - } - (from, to) => todo!("Number layout reshape from {} to {}", from, to), - } - } - - /// Transforms internal value layout. - /// - /// # Returns - /// Transformed number as an optional - or `None` if the operation was impossible without - /// discarding bit information and `wrap` is set to false. - pub fn reshaped(mut self, to: Layout, wrap: bool) -> Option { - self.reshape(to).then_some(self).or(if wrap { Some(self) } else { None }) - } - - #[doc(hidden)] - /// Converts the value into `u1024` integer with the bytes corresponding to the internal - /// representation. - #[inline] - pub(super) fn to_u1024_bytes(self) -> u1024 { self.to_clean().into() } - - #[doc(hidden)] - /// Converts the value into `u1024` integer with the bytes corresponding to the internal - /// representation. - #[inline] - pub(super) fn to_i1024_bytes(self) -> i1024 { self.to_clean().into() } -} - -/// Errors parsing literal values in AluVM assembly code -#[derive(Clone, Eq, PartialEq, Debug, Display, From)] -#[cfg_attr(feature = "std", derive(Error))] -#[display(inner)] -#[non_exhaustive] -pub enum LiteralParseError { - /// Error parsing decimal literal - #[from] - Int(core::num::ParseIntError), - - /// Error parsing float value - #[from] - #[display(Debug)] - Float(amplify::num::apfloat::ParseError), - - /// Unknown literal - #[display("unknown token `{0}` while parsing AluVM assembly literal")] - UnknownLiteral(String), -} - -impl FromStr for Number { - type Err = LiteralParseError; - - fn from_str(s: &str) -> Result { - Ok(if let Some(s) = s.strip_prefix("0x") { - u128::from_str_radix(s, 16)?.into() - } else if let Some(s) = s.strip_prefix("0o") { - u128::from_str_radix(s, 8)?.into() - } else if let Some(s) = s.strip_prefix("0b") { - u128::from_str_radix(s, 2)?.into() - } else if s.starts_with('-') { - i128::from_str(s)?.into() - } else { - u128::from_str(s)?.into() - }) - } -} - -impl Debug for Number { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let len = self.layout.bytes() as usize; - f.debug_struct("Number") - .field("layout", &self.layout) - .field("bytes", { - #[cfg(feature = "std")] - { - use amplify::hex::ToHex; - &self.bytes[..len].to_hex() - } - #[cfg(not(feature = "std"))] - { - &format!("{:#04X?}", &self.bytes[0..len]) - } - }) - .finish() - } -} - -impl Display for Number { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.layout { - Layout::Integer(IntLayout { signed: false, .. }) if self.min_bit_len() <= 12 => { - write!(f, "{}", u16::from(self)) - } - Layout::Integer(IntLayout { signed: false, .. }) if self.min_bit_len() < 16 * 8 => { - write!(f, "0x{:X}", self) - } - Layout::Integer(IntLayout { - signed: true, - bytes, - }) if bytes <= 16 => Display::fmt(&i128::from(self), f), - Layout::Integer(IntLayout { - signed: false, - bytes, - }) if bytes <= 16 => Display::fmt(&u128::from(self), f), - Layout::Integer(IntLayout { - signed: false, - bytes, - }) if bytes <= 32 => Display::fmt(&u256::from(self), f), - Layout::Integer(IntLayout { signed: false, .. }) if self.min_bit_len() < 512 => { - Display::fmt(&u512::from(self), f) - } - Layout::Integer(IntLayout { .. }) => Display::fmt(&u1024::from(self), f), - Layout::Float(FloatLayout::BFloat16) => Display::fmt(&half::bf16::from(self), f), - Layout::Float(FloatLayout::IeeeHalf) => Display::fmt(&ieee::Half::from(self), f), - Layout::Float(FloatLayout::IeeeSingle) => Display::fmt(&ieee::Single::from(self), f), - Layout::Float(FloatLayout::IeeeDouble) => Display::fmt(&ieee::Double::from(self), f), - Layout::Float(FloatLayout::IeeeQuad) => Display::fmt(&ieee::Quad::from(self), f), - Layout::Float(FloatLayout::X87DoubleExt) => { - Display::fmt(&ieee::X87DoubleExtended::from(self), f) - } - _ => { - // TODO(#16) Implement Display for the rest of float layouts - f.write_str("") - } - } - } -} - -impl LowerHex for Number { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - #[cfg(feature = "std")] - use amplify::hex::ToHex; - - match self.layout { - Layout::Integer(IntLayout { - signed: true, - bytes, - }) if bytes <= 16 => LowerHex::fmt(&i128::from(self), f), - Layout::Integer(IntLayout { - signed: false, - bytes, - }) if bytes <= 16 => LowerHex::fmt(&u128::from(self), f), - // TODO(#16) Use LowerHex implementation once it will be done in amplify::num - Layout::Integer(IntLayout { - signed: false, - bytes, - }) if bytes < 32 => { - #[cfg(feature = "std")] - { - f.write_str(u256::from(self).to_be_bytes().to_hex().trim_start_matches('0')) - } - #[cfg(not(feature = "std"))] - { - f.write_str("") - } - } - Layout::Integer(IntLayout { - signed: false, - bytes, - }) if bytes < 32 => { - #[cfg(feature = "std")] - { - f.write_str(u512::from(self).to_be_bytes().to_hex().trim_start_matches('0')) - } - #[cfg(not(feature = "std"))] - { - f.write_str("") - } - } - Layout::Integer(IntLayout { .. }) => { - #[cfg(feature = "std")] - { - f.write_str(u1024::from(self).to_be_bytes().to_hex().trim_start_matches('0')) - } - #[cfg(not(feature = "std"))] - { - f.write_str("") - } - } - // TODO(#16) Use LowerHex implementation once it will be done in `half` crate - /* TODO(#16) Use LowerHex implementation once it will be done in `rustc_apfloat` - Layout::Float(FloatLayout::BFloat16) => LowerHex::fmt(&half::bf16::from(self), f), - Layout::Float(FloatLayout::IeeeHalf) => LowerHex::fmt(&ieee::Half::from(self), f), - Layout::Float(FloatLayout::IeeeSingle) => LowerHex::fmt(&ieee::Single::from(self), f), - Layout::Float(FloatLayout::IeeeDouble) => LowerHex::fmt(&ieee::Double::from(self), f), - Layout::Float(FloatLayout::IeeeQuad) => LowerHex::fmt(&ieee::Quad::from(self), f), - Layout::Float(FloatLayout::X87DoubleExt) => { - LowerHex::fmt(&ieee::X87DoubleExtended::from(self), f) - } - */ - _ => { - // TODO(#16) Implement Display for the rest of float layouts - f.write_str("") - } - } - } -} - -impl UpperHex for Number { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let s = if f.alternate() { format!("{:#x}", self) } else { format!("{:x}", self) }; - f.write_str(&s.to_uppercase()) - } -} - -impl Octal for Number { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.layout { - Layout::Integer(IntLayout { - signed: true, - bytes, - }) if bytes <= 16 => Octal::fmt(&i128::from(self), f), - Layout::Integer(IntLayout { - signed: false, - bytes, - }) if bytes <= 16 => Octal::fmt(&u128::from(self), f), - // TODO(#16) Use LowerHex implementation once it will be done in amplify::num - // TODO(#16) Use LowerHex implementation once it will be done in `half` crate - /* TODO(#16) Use LowerHex implementation once it will be done in `rustc_apfloat` - Layout::Integer(IntLayout { signed: false, bytes }) if bytes < 256 => { - Octal::fmt(&u256::from(self), f) - } - Layout::Integer(IntLayout { signed: false, bytes }) if bytes < 512 => { - Octal::fmt(&u512::from(self), f) - } - Layout::Integer(IntLayout { .. }) => Octal::fmt(&u1024::from(self), f), - Layout::Float(FloatLayout::BFloat16) => Octal::fmt(&half::bf16::from(self), f), - Layout::Float(FloatLayout::IeeeHalf) => Octal::fmt(&ieee::Half::from(self), f), - Layout::Float(FloatLayout::IeeeSingle) => Octal::fmt(&ieee::Single::from(self), f), - Layout::Float(FloatLayout::IeeeDouble) => Octal::fmt(&ieee::Double::from(self), f), - Layout::Float(FloatLayout::IeeeQuad) => Octal::fmt(&ieee::Quad::from(self), f), - Layout::Float(FloatLayout::X87DoubleExt) => { - Octal::fmt(&ieee::X87DoubleExtended::from(self), f) - }*/ - _ => { - // TODO(#16) Implement Display for the rest of float layouts - f.write_str("") - } - } - } -} - -impl LowerExp for Number { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.layout { - Layout::Integer(_) => Display::fmt(self, f), - // TODO(#16) Use LowerHex implementation once it will be done in amplify::num - // TODO(#16) Use LowerHex implementation once it will be done in `half` crate - /* TODO(#16) Use LowerHex implementation once it will be done in `rustc_apfloat` - Layout::Float(FloatLayout::BFloat16) => LowerExp::fmt(&half::bf16::from(self), f), - Layout::Float(FloatLayout::IeeeHalf) => LowerExp::fmt(&ieee::Half::from(self), f), - Layout::Float(FloatLayout::IeeeSingle) => LowerExp::fmt(&ieee::Single::from(self), f), - Layout::Float(FloatLayout::IeeeDouble) => LowerExp::fmt(&ieee::Double::from(self), f), - Layout::Float(FloatLayout::IeeeQuad) => LowerExp::fmt(&ieee::Quad::from(self), f), - Layout::Float(FloatLayout::X87DoubleExt) => { - LowerExp::fmt(&ieee::X87DoubleExtended::from(self), f) - }*/ - _ => { - // TODO(#16) Implement LowerExp for the rest of float layouts - f.write_str("") - } - } - } -} - -impl UpperExp for Number { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let s = if f.alternate() { format!("{:#e}", self) } else { format!("{:e}", self) }; - f.write_str(&s.to_uppercase()) - } -} - -macro_rules! impl_number_bytes_conv { - ($len:literal) => { - impl From for [u8; $len] { - fn from(val: Number) -> Self { - let len = (val.min_bit_len() + 7) as usize / 8; - assert!( - len <= $len, - "attempt to convert number into a byte array with incorrect length", - ); - let mut bytes = [0u8; $len]; - bytes[..len].copy_from_slice(&val.bytes[..len]); - bytes - } - } - - impl From<[u8; $len]> for Number { - fn from(val: [u8; $len]) -> Number { - let mut bytes = [0u8; 1024]; - bytes[0..$len].copy_from_slice(&val[..]); - Number { - layout: Layout::unsigned($len), - bytes, - } - } - } - - impl From<[u8; $len]> for MaybeNumber { - fn from(val: [u8; $len]) -> MaybeNumber { MaybeNumber::from(Number::from(val)) } - } - - impl From> for MaybeNumber { - fn from(val: Option<[u8; $len]>) -> MaybeNumber { - MaybeNumber::from(val.map(Number::from)) - } - } - - impl From<&Option<[u8; $len]>> for MaybeNumber { - fn from(val: &Option<[u8; $len]>) -> MaybeNumber { - MaybeNumber::from(val.map(Number::from)) - } - } - }; -} - -macro_rules! impl_number_int_conv { - ($ty:ident, $len:literal, $signed:expr) => { - impl From for $ty { - fn from(val: Number) -> Self { - assert!( - val.min_bit_len() <= $len * 8, - "attempt to convert Number into type with lower bit dimension" - ); - if $signed { - let mut ret = match val[val.layout.sign_byte()] & 0x80 { - 0 => [0u8; $len], - _ => [255u8; $len], - }; - for i in 0..val.layout.bytes() { - ret[i as usize] = val[i]; - } - $ty::from_le_bytes(ret) - } else { - $ty::from_le_bytes(<[u8; $len]>::from(val)) - } - } - } - - impl From<&$ty> for Number { - fn from(val: &$ty) -> Self { - let mut bytes = [0u8; 1024]; - let le = val.to_le_bytes(); - bytes[0..le.len()].copy_from_slice(&le[..]); - if $signed { - Number { - layout: Layout::signed(le.len() as u16), - bytes, - } - } else { - Number { - layout: Layout::unsigned(le.len() as u16), - bytes, - } - } - } - } - - impl From<&Number> for $ty { - fn from(val: &Number) -> Self { $ty::from(*val) } - } - - impl From<$ty> for Number { - fn from(val: $ty) -> Self { Number::from(&val) } - } - - impl From<$ty> for MaybeNumber { - fn from(val: $ty) -> Self { MaybeNumber::some(Number::from(val)) } - } - impl From<&$ty> for MaybeNumber { - fn from(val: &$ty) -> Self { MaybeNumber::some(Number::from(*val)) } - } - impl From> for MaybeNumber { - fn from(val: Option<$ty>) -> Self { MaybeNumber::from(val.map(Number::from)) } - } - impl From> for MaybeNumber { - fn from(val: Option<&$ty>) -> Self { MaybeNumber::from(val.copied().map(Number::from)) } - } - impl From<&Option<$ty>> for MaybeNumber { - fn from(val: &Option<$ty>) -> Self { MaybeNumber::from((*val).map(Number::from)) } - } - }; -} - -macro_rules! impl_number_float_conv { - ($ty:ident, $tys:ident, $len:literal, $layout:ident) => { - impl From for $ty { - fn from(val: Number) -> Self { - assert!( - val.min_bit_len() <= $len * 8, - "attempt to convert Number into type with lower bit dimension" - ); - $ty::from_bits(val.into()) - } - } - - impl From<&Number> for $ty { - fn from(val: &Number) -> Self { $ty::from(*val) } - } - - impl From<$ty> for MaybeNumber { - fn from(mut val: $ty) -> Self { - if val.is_nan() { - return MaybeNumber::none(); - } - if val == -$ty::ZERO { - val = $ty::ZERO - } - let mut bytes = [0u8; 1024]; - let le = val.to_bits().to_le_bytes(); - bytes[0..le.len()].copy_from_slice(&le[..]); - MaybeNumber::some(Number { - layout: Layout::float(FloatLayout::$layout), - bytes, - }) - } - } - }; -} - -impl> From> for MaybeNumber { - fn from(init: StatusAnd) -> Self { - match init.status { - Status::OK | Status::INEXACT => init.value.into(), - _ => MaybeNumber::none(), - } - } -} - -impl_number_bytes_conv!(1); -impl_number_bytes_conv!(2); -impl_number_bytes_conv!(4); -impl_number_bytes_conv!(8); -impl_number_bytes_conv!(16); -impl_number_bytes_conv!(20); -impl_number_bytes_conv!(32); -impl_number_bytes_conv!(64); -impl_number_bytes_conv!(128); -impl_number_bytes_conv!(256); -impl_number_bytes_conv!(512); -impl_number_bytes_conv!(1024); - -impl_number_int_conv!(u8, 1, false); -impl_number_int_conv!(u16, 2, false); -impl_number_int_conv!(u32, 4, false); -impl_number_int_conv!(u64, 8, false); -impl_number_int_conv!(u128, 16, false); -impl_number_int_conv!(u256, 32, false); -impl_number_int_conv!(u512, 64, false); -impl_number_int_conv!(u1024, 128, false); - -mod _float_impl { - use amplify::num::apfloat::ieee::*; - - use super::*; - - impl_number_float_conv!(bf16, bf16, 2, BFloat16); - impl_number_float_conv!(Half, HalfS, 2, IeeeHalf); - impl_number_float_conv!(Single, SingleS, 4, IeeeSingle); - impl_number_float_conv!(Double, DoubleS, 8, IeeeDouble); - impl_number_float_conv!(X87DoubleExtended, X87DoubleExtendedS, 10, X87DoubleExt); - impl_number_float_conv!(Quad, QuadS, 16, IeeeQuad); - impl_number_float_conv!(Oct, OctS, 32, IeeeOct); -} - -impl_number_int_conv!(i8, 1, true); -impl_number_int_conv!(i16, 2, true); -impl_number_int_conv!(i32, 4, true); -impl_number_int_conv!(i64, 8, true); -impl_number_int_conv!(i128, 16, true); -impl_number_int_conv!(i256, 32, true); -impl_number_int_conv!(i512, 64, true); -impl_number_int_conv!(i1024, 128, true); - -/// Value for step instructions which can be displayed as a part of operation mnemonic -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, From)] -pub struct Step(#[from] i8); - -impl Step { - /// Constructs step from a value - pub fn with(val: i8) -> Self { Self(val) } - - /// Returns step value - pub fn as_i8(self) -> i8 { self.0 } -} - -impl From for Number { - #[inline] - fn from(step: Step) -> Self { Number::from(step.0) } -} - -impl Display for Step { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let val = self.0; - if f.alternate() { - match val { - 1 => f.write_str("inc"), - -1 => f.write_str("dec"), - x if x < 0 => f.write_str("sub"), - x if x >= 0 => f.write_str("add"), - _ => unreachable!(), - } - } else if val.abs() > 1 { - Display::fmt(&if val >= 0 { val } else { -val }, f)?; - f.write_char(',') - } else { - Ok(()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn bytes_conv_test() { - assert_eq!([255u8], <[u8; 1]>::from(Number::from(255u8))); - } - - #[test] - fn asserting_layouts_kinds() { - let signed_integer_layout = Layout::Integer(IntLayout::signed(33)); - assert!(signed_integer_layout.is_signed_int()); - assert!(!signed_integer_layout.is_unsigned_int()); - assert!(!signed_integer_layout.is_float()); - - let unsigned_integer_layout = Layout::Integer(IntLayout::unsigned(33)); - assert!(unsigned_integer_layout.is_unsigned_int()); - assert!(!unsigned_integer_layout.is_signed_int()); - assert!(!unsigned_integer_layout.is_float()); - - let float_layout = Layout::Float(FloatLayout::BFloat16); - assert!(float_layout.is_float()); - assert!(!float_layout.is_signed_int()); - assert!(!float_layout.is_unsigned_int()); - } - - #[test] - fn returning_bytes() { - let signed_integer_layout = Layout::Integer(IntLayout::signed(3)); - assert_eq!(signed_integer_layout.bytes(), 3); - - let unsigned_integer_layout = Layout::Integer(IntLayout::unsigned(3)); - assert_eq!(unsigned_integer_layout.bytes(), 3); - - let float_layout = Layout::Float(FloatLayout::BFloat16); - assert_eq!(float_layout.bytes(), 2); - } - - #[test] - fn is_zero_test() { - let num = Number::from(0); - assert!(num.is_zero()); - let num = Number::from(1); - assert!(!num.is_zero()); - } - - #[test] - fn is_unsigned_int_test() { - let num = Number::from(0u8); - assert!(num.layout.is_unsigned_int()); - let num = Number::from(0i8); - assert!(!num.layout.is_unsigned_int()); - let num = Number::from(1u16); - assert!(num.layout.is_unsigned_int()); - let num = Number::from(1i16); - assert!(!num.layout.is_unsigned_int()); - let num = Number::from(-1); - assert!(!num.layout.is_unsigned_int()); - } - - #[test] - fn is_positive_test() { - let num = Number::from(1); - assert!(num.is_positive()); - let num = Number::from(0); - assert!(!num.is_positive()); - let num = Number::from(-1); - assert!(!num.is_positive()); - let num = Number::from(127); - assert!(num.is_positive()); - } - - #[test] - fn reshape_test() { - let mut x = Number::with( - [1u8], - Layout::Integer(IntLayout { - signed: false, - bytes: 1, - }), - ) - .unwrap(); - let y = Number::with( - [1u8, 0u8], - Layout::Integer(IntLayout { - signed: false, - bytes: 2, - }), - ) - .unwrap(); - assert!(x.reshape(Layout::Integer(IntLayout { - signed: false, - bytes: 2 - }))); - assert_eq!(x, y); - } - - #[test] - fn reshape_with_same_layout_test() { - let mut x = Number::with( - [1u8], - Layout::Integer(IntLayout { - signed: false, - bytes: 1, - }), - ) - .unwrap(); - let y = Number::with( - [1u8], - Layout::Integer(IntLayout { - signed: false, - bytes: 1, - }), - ) - .unwrap(); - assert!(x.reshape(Layout::Integer(IntLayout { - signed: false, - bytes: 1 - }))); - assert_eq!(x, y); - } - - #[test] - fn reshape_negative_value_test() { - let mut x = Number::from(-24i8); - let y = Number::from(-24i16); - let z = Number::from(-24i128); - assert_eq!( - x.layout, - Layout::Integer(IntLayout { - signed: true, - bytes: 1 - }) - ); - assert!(x.reshape(Layout::Integer(IntLayout { - signed: true, - bytes: 2 - }))); - assert_eq!(x, y); - assert!(x.reshape(Layout::Integer(IntLayout { - signed: true, - bytes: 16 - }))); - assert_eq!(x, z); - } - - #[test] - fn take_sign_test() { - let x = Number::from(-1i8); - let y = Number::from(255u8); - let z = MaybeNumber::from(ieee::Single::SMALLEST).unwrap(); - assert_eq!(x.into_unsigned(), y); - assert_eq!(x.into_unsigned().into_signed(), x); - assert_eq!(z.into_unsigned(), z); - assert_eq!(z.into_signed(), z); - } -} diff --git a/src/isa/arch.rs b/src/isa/arch.rs new file mode 100644 index 0000000..c0be7a8 --- /dev/null +++ b/src/isa/arch.rs @@ -0,0 +1,79 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use core::fmt::Debug; + +use strict_encoding::stl::AlphaCapsNum; +use strict_encoding::{RString, StrictDumb}; + +use super::CtrlInstr; +use crate::core::SiteId; +use crate::LIB_NAME_ALUVM; + +pub const ISA_ID_MAX_LEN: usize = 16; + +#[macro_export] +macro_rules! isa { + ($id:literal) => { + $crate::IsaId::from($id) + }; + ($id:ident) => { + $crate::IsaId::from($id) + }; +} + +#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[wrapper(Deref, Display, FromStr)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_ALUVM)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))] +pub struct IsaId(RString); + +impl StrictDumb for IsaId { + fn strict_dumb() -> Self { Self::from("DUMB") } +} + +impl From<&'static str> for IsaId { + fn from(id: &'static str) -> Self { Self(RString::from(id)) } +} + +/// Reserved instruction, which equal to [`crate::ExecStep::FailHalt`]. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Default)] +#[display("halt {0:#02X}#h")] +pub struct ReservedInstr(/** Reserved instruction op code value */ pub(super) u8); + +/// Complete AluVM ISA. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display, From)] +#[display(inner)] +pub enum Instr { + /// Control flow instructions. + #[from] + Ctrl(CtrlInstr), + + // #[cfg(feature = "str")] + // Str(array::instr::StrInstr), + /// Reserved instruction for future use in core `ALU` ISAs. + #[from] + Reserved(ReservedInstr), +} diff --git a/src/isa/bytecode.rs b/src/isa/bytecode.rs index 580cc2e..69d107b 100644 --- a/src/isa/bytecode.rs +++ b/src/isa/bytecode.rs @@ -3,1316 +3,198 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. -//! Instruction serialization and deserialization from bytecode. - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; +use core::fmt::Debug; use core::ops::RangeInclusive; -use amplify::num::{u1, u2, u3, u5}; - -use super::opcodes::*; -use super::{ - ArithmeticOp, BitwiseOp, BytesOp, CmpOp, ControlFlowOp, Curve25519Op, DigestOp, Instr, - InstructionSet, MoveOp, PutOp, ReservedOp, Secp256k1Op, -}; -use crate::data::{ByteStr, MaybeNumber}; -use crate::library::{CodeEofError, LibSite, Read, Write, WriteError}; -use crate::reg::RegBlockAR; +use amplify::confinement::SmallBlob; +use amplify::num::{u1, u2, u3, u4, u5, u6, u7}; -/// Errors encoding instructions -#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] -#[display(doc_comments)] -pub enum BytecodeError { - /// Write error - #[display(inner)] - #[from] - Write(WriteError), +use crate::core::SiteId; - /// put operation does not contain number (when it was deserialized, the data segment was - /// shorter than the number value offset to read) - PutNoNumber, -} +/// Non-failing byte encoding for the instruction set. +/// +/// We can't use `io` since (1) we are no_std, (2) it operates data with unlimited length (while we +/// are bound by u16), (3) it provides too many fails in situations when we can't fail because of +/// `u16`-bounding and exclusive in-memory encoding handling. +pub trait Bytecode { + /// Returns range of instruction bytecodes covered by a set of operations. + fn op_range() -> RangeInclusive; -#[cfg(feature = "std")] -impl ::std::error::Error for BytecodeError { - fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { - match self { - BytecodeError::Write(err) => Some(err), - BytecodeError::PutNoNumber => None, - } - } -} + /// Returns byte representing instruction code (without its arguments). + fn opcode_byte(&self) -> u8; -/// Non-failiable byte encoding for the instruction set. We can't use `io` since -/// (1) we are no_std, (2) it operates data with unlimited length (while we are -/// bound by u16), (3) it provides too many fails in situations when we can't -/// fail because of `u16`-bounding and exclusive in-memory encoding handling. -pub trait Bytecode { - /// Returns range of instruction btecodes covered by a set of operations - fn instr_range() -> RangeInclusive; - - /// Returns byte representing instruction code (without its arguments) - fn instr_byte(&self) -> u8; - - /// If the instruction call or references any external library, returns the call site in that - /// library. + /// If the instruction call or references any external program, returns a reference to it. #[inline] - fn call_site(&self) -> Option { None } + fn external_ref(&self) -> Option { None } - /// Writes the instruction as bytecode - fn encode(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - writer.write_u8(self.instr_byte())?; - self.encode_args(writer) + /// Write an instruction as bytecode. + fn encode_instr(&self, writer: &mut W) -> Result<(), W::Error> + where W: BytecodeWrite { + writer.write_byte(self.opcode_byte())?; + self.encode_operands(writer)?; + writer.check_aligned(); + Ok(()) } - /// Writes instruction arguments as bytecode, omitting instruction code byte - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write; + /// Writes an instruction operands as bytecode, omitting opcode byte. + fn encode_operands(&self, writer: &mut W) -> Result<(), W::Error> + where W: BytecodeWrite; - /// Reads the instruction from bytecode - fn decode(reader: &mut R) -> Result + /// Reads an instruction from bytecode. + fn decode_instr(reader: &mut R) -> Result where Self: Sized, - R: Read; -} - -impl Bytecode for Instr -where Extension: InstructionSet -{ - #[inline] - fn instr_range() -> RangeInclusive { 0..=u8::MAX } - - fn instr_byte(&self) -> u8 { - match self { - Instr::ControlFlow(instr) => instr.instr_byte(), - Instr::Put(instr) => instr.instr_byte(), - Instr::Move(instr) => instr.instr_byte(), - Instr::Cmp(instr) => instr.instr_byte(), - Instr::Arithmetic(instr) => instr.instr_byte(), - Instr::Bitwise(instr) => instr.instr_byte(), - Instr::Bytes(instr) => instr.instr_byte(), - Instr::Digest(instr) => instr.instr_byte(), - #[cfg(feature = "secp256k1")] - Instr::Secp256k1(instr) => instr.instr_byte(), - #[cfg(feature = "curve25519")] - Instr::Curve25519(instr) => instr.instr_byte(), - Instr::ExtensionCodes(instr) => instr.instr_byte(), - Instr::ReservedInstruction(instr) => instr.instr_byte(), - Instr::Nop => 1, - } - } - - fn call_site(&self) -> Option { - match self { - Instr::ControlFlow(instr) => instr.call_site(), - Instr::Put(instr) => instr.call_site(), - Instr::Move(instr) => instr.call_site(), - Instr::Cmp(instr) => instr.call_site(), - Instr::Arithmetic(instr) => instr.call_site(), - Instr::Bitwise(instr) => instr.call_site(), - Instr::Bytes(instr) => instr.call_site(), - Instr::Digest(instr) => instr.call_site(), - #[cfg(feature = "secp256k1")] - Instr::Secp256k1(instr) => instr.call_site(), - #[cfg(feature = "curve25519")] - Instr::Curve25519(instr) => instr.call_site(), - Instr::ExtensionCodes(instr) => instr.call_site(), - Instr::ReservedInstruction(instr) => instr.call_site(), - Instr::Nop => None, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - Instr::ControlFlow(instr) => instr.encode_args(writer), - Instr::Put(instr) => instr.encode_args(writer), - Instr::Move(instr) => instr.encode_args(writer), - Instr::Cmp(instr) => instr.encode_args(writer), - Instr::Arithmetic(instr) => instr.encode_args(writer), - Instr::Bitwise(instr) => instr.encode_args(writer), - Instr::Bytes(instr) => instr.encode_args(writer), - Instr::Digest(instr) => instr.encode_args(writer), - #[cfg(feature = "secp256k1")] - Instr::Secp256k1(instr) => instr.encode_args(writer), - #[cfg(feature = "curve25519")] - Instr::Curve25519(instr) => instr.encode_args(writer), - Instr::ExtensionCodes(instr) => instr.encode_args(writer), - Instr::ReservedInstruction(instr) => instr.encode_args(writer), - Instr::Nop => Ok(()), - } - } - - fn decode(reader: &mut R) -> Result - where R: Read { - let instr = reader.peek_u8()?; - Ok(match instr { - instr if ControlFlowOp::instr_range().contains(&instr) => { - Instr::ControlFlow(ControlFlowOp::decode(reader)?) - } - instr if PutOp::instr_range().contains(&instr) => Instr::Put(PutOp::decode(reader)?), - instr if MoveOp::instr_range().contains(&instr) => Instr::Move(MoveOp::decode(reader)?), - instr if CmpOp::instr_range().contains(&instr) => Instr::Cmp(CmpOp::decode(reader)?), - instr if ArithmeticOp::instr_range().contains(&instr) => { - Instr::Arithmetic(ArithmeticOp::decode(reader)?) - } - instr if BitwiseOp::instr_range().contains(&instr) => { - Instr::Bitwise(BitwiseOp::decode(reader)?) - } - instr if BytesOp::instr_range().contains(&instr) => { - Instr::Bytes(BytesOp::decode(reader)?) - } - instr if DigestOp::instr_range().contains(&instr) => { - Instr::Digest(DigestOp::decode(reader)?) - } - #[cfg(feature = "secp256k1")] - instr if Secp256k1Op::instr_range().contains(&instr) => { - Instr::Secp256k1(Secp256k1Op::decode(reader)?) - } - #[cfg(feature = "curve25519")] - instr if Curve25519Op::instr_range().contains(&instr) => { - Instr::Curve25519(Curve25519Op::decode(reader)?) - } - INSTR_RESV_FROM..=INSTR_RESV_TO => { - Instr::ReservedInstruction(ReservedOp::decode(reader)?) - } - INSTR_NOP => Instr::Nop, - INSTR_ISAE_FROM..=INSTR_ISAE_TO => Instr::ExtensionCodes(Extension::decode(reader)?), - x => unreachable!("unable to classify instruction {:#010b}", x), - }) - } -} - -impl Bytecode for ControlFlowOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_FAIL..=INSTR_RET } - - fn instr_byte(&self) -> u8 { - match self { - ControlFlowOp::Fail => INSTR_FAIL, - ControlFlowOp::Test => INSTR_TEST, - ControlFlowOp::Jmp(_) => INSTR_JMP, - ControlFlowOp::Jif(_) => INSTR_JIF, - ControlFlowOp::Routine(_) => INSTR_ROUTINE, - ControlFlowOp::Call(_) => INSTR_CALL, - ControlFlowOp::Exec(_) => INSTR_EXEC, - ControlFlowOp::Ret => INSTR_RET, - } - } - - #[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 { - match self { - ControlFlowOp::Fail => {} - ControlFlowOp::Test => {} - ControlFlowOp::Jmp(pos) | ControlFlowOp::Jif(pos) | ControlFlowOp::Routine(pos) => { - writer.write_u16(*pos)? - } - ControlFlowOp::Call(lib_site) | ControlFlowOp::Exec(lib_site) => { - writer.write_u16(lib_site.pos)?; - writer.write_lib(lib_site.lib)?; - } - ControlFlowOp::Ret => {} - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - Ok(match reader.read_u8()? { - INSTR_FAIL => Self::Fail, - INSTR_TEST => Self::Test, - INSTR_JMP => Self::Jmp(reader.read_u16()?), - INSTR_JIF => Self::Jif(reader.read_u16()?), - INSTR_ROUTINE => Self::Routine(reader.read_u16()?), - INSTR_CALL => Self::Call(LibSite::with(reader.read_u16()?, reader.read_lib()?)), - INSTR_EXEC => Self::Exec(LibSite::with(reader.read_u16()?, reader.read_lib()?)), - INSTR_RET => Self::Ret, - x => unreachable!("instruction {:#010b} classified as control flow operation", x), - }) - } -} - -impl Bytecode for PutOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_CLRA..=INSTR_PUTIFR } - - fn instr_byte(&self) -> u8 { - match self { - PutOp::ClrA(_, _) => INSTR_CLRA, - PutOp::ClrF(_, _) => INSTR_CLRF, - PutOp::ClrR(_, _) => INSTR_CLRR, - PutOp::PutA(_, _, _) => INSTR_PUTA, - PutOp::PutF(_, _, _) => INSTR_PUTF, - PutOp::PutR(_, _, _) => INSTR_PUTR, - PutOp::PutIfA(_, _, _) => INSTR_PUTIFA, - PutOp::PutIfR(_, _, _) => INSTR_PUTIFR, - } + R: BytecodeRead, + { + let opcode = reader.read_byte()?; + let instr = Self::decode_operands(reader, opcode)?; + reader.check_aligned(); + Ok(instr) } - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - PutOp::ClrA(reg, idx) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - PutOp::ClrF(reg, idx) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - PutOp::ClrR(reg, idx) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - PutOp::PutA(reg, reg32, val) | PutOp::PutIfA(reg, reg32, val) => { - writer.write_u3(reg)?; - writer.write_u5(reg32)?; - if let Some(value) = ***val { - writer.write_number(*reg, value)?; - } else { - return Err(BytecodeError::PutNoNumber); - } - } - PutOp::PutF(reg, reg32, val) => { - writer.write_u3(reg)?; - writer.write_u5(reg32)?; - if let Some(value) = ***val { - writer.write_number(*reg, value)?; - } else { - return Err(BytecodeError::PutNoNumber); - } - } - PutOp::PutR(reg, reg32, val) | PutOp::PutIfR(reg, reg32, val) => { - writer.write_u3(reg)?; - writer.write_u5(reg32)?; - if let Some(value) = ***val { - writer.write_number(*reg, value)?; - } else { - return Err(BytecodeError::PutNoNumber); - } - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - let instr = reader.read_u8()?; - let reg = reader.read_u3()?; - let index = reader.read_u5()?.into(); - Ok(match instr { - INSTR_CLRA => Self::ClrA(reg.into(), index), - INSTR_CLRF => Self::ClrF(reg.into(), index), - INSTR_CLRR => Self::ClrR(reg.into(), index), - _ => match instr { - INSTR_PUTA => { - let reg = reg.into(); - let value = Box::new( - reader.read_number(reg).map(MaybeNumber::some).unwrap_or_default(), - ); - Self::PutA(reg, index, value) - } - INSTR_PUTF => { - let reg = reg.into(); - let value = Box::new( - reader.read_number(reg).map(MaybeNumber::some).unwrap_or_default(), - ); - Self::PutF(reg, index, value) - } - INSTR_PUTR => { - let reg = reg.into(); - let value = Box::new( - reader.read_number(reg).map(MaybeNumber::some).unwrap_or_default(), - ); - Self::PutR(reg, index, value) - } - INSTR_PUTIFA => { - let reg = reg.into(); - let value = Box::new( - reader.read_number(reg).map(MaybeNumber::some).unwrap_or_default(), - ); - Self::PutIfA(reg, index, value) - } - INSTR_PUTIFR => { - let reg = reg.into(); - let value = Box::new( - reader.read_number(reg).map(MaybeNumber::some).unwrap_or_default(), - ); - Self::PutIfR(reg, index, value) - } - x => unreachable!("instruction {:#010b} classified as put operation", x), - }, - }) - } -} - -impl Bytecode for MoveOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_MOV..=INSTR_CFA } - - fn instr_byte(&self) -> u8 { - match self { - MoveOp::MovA(_, _, _) - | MoveOp::DupA(_, _, _) - | MoveOp::SwpA(_, _, _) - | MoveOp::MovF(_, _, _) - | MoveOp::DupF(_, _, _) - | MoveOp::SwpF(_, _, _) - | MoveOp::MovR(_, _, _) - | MoveOp::DupR(_, _, _) => INSTR_MOV, - MoveOp::CpyA(_, _, _, _) => INSTR_CPA, - MoveOp::CnvA(_, _, _, _) => INSTR_CNA, - MoveOp::CnvF(_, _, _, _) => INSTR_CNF, - MoveOp::CpyR(_, _, _, _) => INSTR_CPR, - MoveOp::SpyAR(_, _, _, _) => INSTR_SPY, - MoveOp::CnvAF(_, _, _, _) => INSTR_CAF, - MoveOp::CnvFA(_, _, _, _) => INSTR_CFA, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - MoveOp::MovA(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b000))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::DupA(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b001))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::SwpA(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b010))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::MovF(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b011))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::DupF(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b100))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::SwpF(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b101))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::MovR(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b110))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::DupR(reg, idx1, idx2) => { - writer.write_u3(u3::with(0b111))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - MoveOp::CpyA(sreg, sidx, dreg, didx) => { - writer.write_u3(sreg)?; - writer.write_u5(sidx)?; - writer.write_u3(dreg)?; - writer.write_u5(didx)?; - } - MoveOp::CnvA(sreg, sidx, dreg, didx) => { - writer.write_u3(sreg)?; - writer.write_u5(sidx)?; - writer.write_u3(dreg)?; - writer.write_u5(didx)?; - } - MoveOp::CnvF(sreg, sidx, dreg, didx) => { - writer.write_u3(sreg)?; - writer.write_u5(sidx)?; - writer.write_u3(dreg)?; - writer.write_u5(didx)?; - } - MoveOp::CpyR(sreg, sidx, dreg, didx) => { - writer.write_u3(sreg)?; - writer.write_u5(sidx)?; - writer.write_u3(dreg)?; - writer.write_u5(didx)?; - } - MoveOp::SpyAR(sreg, sidx, dreg, didx) => { - writer.write_u3(sreg)?; - writer.write_u5(sidx)?; - writer.write_u3(dreg)?; - writer.write_u5(didx)?; - } - MoveOp::CnvAF(sreg, sidx, dreg, didx) => { - writer.write_u3(sreg)?; - writer.write_u5(sidx)?; - writer.write_u3(dreg)?; - writer.write_u5(didx)?; - } - MoveOp::CnvFA(sreg, sidx, dreg, didx) => { - writer.write_u3(sreg)?; - writer.write_u5(sidx)?; - writer.write_u3(dreg)?; - writer.write_u5(didx)?; - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - let instr = reader.read_u8()?; - - Ok(if instr == INSTR_MOV { - let code = reader.read_u3()?; - let idx1 = reader.read_u5()?.into(); - let idx2 = reader.read_u5()?.into(); - let reg = reader.read_u3()?; - match code.to_u8() { - 0b000 => MoveOp::MovA(reg.into(), idx1, idx2), - 0b001 => MoveOp::DupA(reg.into(), idx1, idx2), - 0b010 => MoveOp::SwpA(reg.into(), idx1, idx2), - 0b011 => MoveOp::MovF(reg.into(), idx1, idx2), - 0b100 => MoveOp::DupF(reg.into(), idx1, idx2), - 0b101 => MoveOp::SwpF(reg.into(), idx1, idx2), - 0b110 => MoveOp::MovR(reg.into(), idx1, idx2), - 0b111 => MoveOp::DupR(reg.into(), idx1, idx2), - _ => unreachable!(), - } - } else { - let sreg = reader.read_u3()?; - let sidx = reader.read_u5()?.into(); - let dreg = reader.read_u3()?; - let didx = reader.read_u5()?.into(); - match instr { - INSTR_CPA => MoveOp::CpyA(sreg.into(), sidx, dreg.into(), didx), - INSTR_CNA => MoveOp::CnvA(sreg.into(), sidx, dreg.into(), didx), - INSTR_CNF => MoveOp::CnvF(sreg.into(), sidx, dreg.into(), didx), - INSTR_CPR => MoveOp::CpyR(sreg.into(), sidx, dreg.into(), didx), - INSTR_SPY => MoveOp::SpyAR(sreg.into(), sidx, dreg.into(), didx), - INSTR_CAF => MoveOp::CnvAF(sreg.into(), sidx, dreg.into(), didx), - INSTR_CFA => MoveOp::CnvFA(sreg.into(), sidx, dreg.into(), didx), - x => unreachable!("instruction {:#010b} classified as move operation", x), - } - }) - } -} - -impl Bytecode for CmpOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_LGT..=INSTR_STINV } - - fn instr_byte(&self) -> u8 { - match self { - CmpOp::GtA(_, _, _, _) - | CmpOp::LtA(_, _, _, _) - | CmpOp::GtF(_, _, _, _) - | CmpOp::LtF(_, _, _, _) => INSTR_LGT, - CmpOp::GtR(_, _, _) - | CmpOp::LtR(_, _, _) - | CmpOp::EqA(_, _, _, _) - | CmpOp::EqF(_, _, _, _) - | CmpOp::EqR(_, _, _, _) => INSTR_CMP, - CmpOp::IfZA(_, _) => INSTR_IFZA, - CmpOp::IfZR(_, _) => INSTR_IFZR, - CmpOp::IfNA(_, _) => INSTR_IFNA, - CmpOp::IfNR(_, _) => INSTR_IFNR, - CmpOp::St(_, _, _) => INSTR_ST, - CmpOp::StInv => INSTR_STINV, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - CmpOp::GtA(flag, reg, idx1, idx2) => { - writer.write_u2(u2::with(0b00))?; - writer.write_u1(flag)?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - CmpOp::LtA(flag, reg, idx1, idx2) => { - writer.write_u2(u2::with(0b01))?; - writer.write_u1(flag)?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - CmpOp::GtF(flag, reg, idx1, idx2) => { - writer.write_u2(u2::with(0b10))?; - writer.write_u1(flag)?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - CmpOp::LtF(flag, reg, idx1, idx2) => { - writer.write_u2(u2::with(0b11))?; - writer.write_u1(flag)?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - - CmpOp::GtR(reg, idx1, idx2) => { - writer.write_u2(u2::with(0b00))?; - writer.write_u1(u1::with(0b0))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - CmpOp::LtR(reg, idx1, idx2) => { - writer.write_u2(u2::with(0b00))?; - writer.write_u1(u1::with(0b1))?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - CmpOp::EqA(flag, reg, idx1, idx2) => { - writer.write_u2(u2::with(0b01))?; - writer.write_u1(flag)?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - CmpOp::EqF(flag, reg, idx1, idx2) => { - writer.write_u2(u2::with(0b10))?; - writer.write_u1(flag)?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - CmpOp::EqR(flag, reg, idx1, idx2) => { - writer.write_u2(u2::with(0b11))?; - writer.write_u1(flag)?; - writer.write_u5(idx1)?; - writer.write_u5(idx2)?; - writer.write_u3(reg)?; - } - - CmpOp::IfZA(reg, idx) | CmpOp::IfNA(reg, idx) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - CmpOp::IfZR(reg, idx) | CmpOp::IfNR(reg, idx) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - CmpOp::St(flag, reg, idx) => { - writer.write_u2(flag)?; - writer.write_u3(reg)?; - writer.write_u3(idx)?; - } - CmpOp::StInv => {} - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - let instr = reader.read_u8()?; - - Ok(if instr == INSTR_LGT || instr == INSTR_CMP { - let code = reader.read_u2()?; - let flag = reader.read_u1()?; - let idx1 = reader.read_u5()?.into(); - let idx2 = reader.read_u5()?.into(); - let reg = reader.read_u3()?; - match (instr, code.to_u8(), flag.into_u8()) { - (INSTR_LGT, 0b00, _) => CmpOp::GtA(flag.into(), reg.into(), idx1, idx2), - (INSTR_LGT, 0b01, _) => CmpOp::LtA(flag.into(), reg.into(), idx1, idx2), - (INSTR_LGT, 0b10, _) => CmpOp::GtF(flag.into(), reg.into(), idx1, idx2), - (INSTR_LGT, 0b11, _) => CmpOp::LtF(flag.into(), reg.into(), idx1, idx2), - (INSTR_CMP, 0b00, 0b0) => CmpOp::GtR(reg.into(), idx1, idx2), - (INSTR_CMP, 0b00, 0b1) => CmpOp::LtR(reg.into(), idx1, idx2), - (INSTR_CMP, 0b01, _) => CmpOp::EqA(flag.into(), reg.into(), idx1, idx2), - (INSTR_CMP, 0b10, _) => CmpOp::EqF(flag.into(), reg.into(), idx1, idx2), - (INSTR_CMP, 0b11, _) => CmpOp::EqR(flag.into(), reg.into(), idx1, idx2), - _ => unreachable!(), - } - } else if instr == INSTR_STINV { - CmpOp::StInv - } else if instr == INSTR_ST { - CmpOp::St(reader.read_u2()?.into(), reader.read_u3()?.into(), reader.read_u3()?.into()) - } else { - let reg = reader.read_u3()?; - let idx = reader.read_u5()?.into(); - match instr { - INSTR_IFZA => CmpOp::IfZA(reg.into(), idx), - INSTR_IFNA => CmpOp::IfNA(reg.into(), idx), - INSTR_IFZR => CmpOp::IfZR(reg.into(), idx), - INSTR_IFNR => CmpOp::IfNR(reg.into(), idx), - x => unreachable!("instruction {:#010b} classified as comparison operation", x), - } - }) - } -} - -impl Bytecode for ArithmeticOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_ADD..=INSTR_REM } - - fn instr_byte(&self) -> u8 { - match self { - ArithmeticOp::AddF(_, _, _, _) | ArithmeticOp::AddA(_, _, _, _) => INSTR_ADD, - ArithmeticOp::SubF(_, _, _, _) | ArithmeticOp::SubA(_, _, _, _) => INSTR_SUB, - ArithmeticOp::MulF(_, _, _, _) | ArithmeticOp::MulA(_, _, _, _) => INSTR_MUL, - ArithmeticOp::DivF(_, _, _, _) | ArithmeticOp::DivA(_, _, _, _) => INSTR_DIV, - ArithmeticOp::Rem(_, _, _, _) => INSTR_REM, - ArithmeticOp::Stp(_, _, _) => INSTR_STP, - ArithmeticOp::Neg(_, _) => INSTR_NEG, - ArithmeticOp::Abs(_, _) => INSTR_ABS, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - ArithmeticOp::Neg(reg, idx) | ArithmeticOp::Abs(reg, idx) => { - writer.write_u4(reg)?; - writer.write_u4(idx)?; - } - ArithmeticOp::Stp(reg, idx, step) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - writer.write_i8(step.as_i8())?; - } - ArithmeticOp::AddA(flags, reg, src1, src2) - | ArithmeticOp::SubA(flags, reg, src1, src2) - | ArithmeticOp::MulA(flags, reg, src1, src2) - | ArithmeticOp::DivA(flags, reg, src1, src2) => { - writer.write_u1(u1::with(0b0))?; - writer.write_u2(flags)?; - writer.write_u5(src1)?; - writer.write_u5(src2)?; - writer.write_u3(reg)?; - } - ArithmeticOp::AddF(flag, reg, src1, src2) - | ArithmeticOp::SubF(flag, reg, src1, src2) - | ArithmeticOp::MulF(flag, reg, src1, src2) - | ArithmeticOp::DivF(flag, reg, src1, src2) => { - writer.write_u1(u1::with(0b1))?; - writer.write_u2(flag)?; - writer.write_u5(src1)?; - writer.write_u5(src2)?; - writer.write_u3(reg)?; - } - ArithmeticOp::Rem(reg1, src1, reg2, src2) => { - writer.write_u3(reg1)?; - writer.write_u5(src1)?; - writer.write_u3(reg2)?; - writer.write_u5(src2)?; - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - let instr = reader.read_u8()?; - - Ok(if (INSTR_ADD..=INSTR_DIV).contains(&instr) { - let code = reader.read_u1()?.into(); - let flags = reader.read_u2()?; - let src1 = reader.read_u5()?.into(); - let src2 = reader.read_u5()?.into(); - let reg = reader.read_u3()?; - match (code, instr) { - (0b0, INSTR_ADD) => Self::AddA(flags.into(), reg.into(), src1, src2), - (0b0, INSTR_SUB) => Self::SubA(flags.into(), reg.into(), src1, src2), - (0b0, INSTR_MUL) => Self::MulA(flags.into(), reg.into(), src1, src2), - (0b0, INSTR_DIV) => Self::DivA(flags.into(), reg.into(), src1, src2), - (0b1, INSTR_ADD) => Self::AddF(flags.into(), reg.into(), src1, src2), - (0b1, INSTR_SUB) => Self::SubF(flags.into(), reg.into(), src1, src2), - (0b1, INSTR_MUL) => Self::MulF(flags.into(), reg.into(), src1, src2), - (0b1, INSTR_DIV) => Self::DivF(flags.into(), reg.into(), src1, src2), - _ => unreachable!(), - } - } else { - match instr { - INSTR_NEG => Self::Neg(reader.read_u4()?.into(), reader.read_u4()?.into()), - INSTR_STP => { - let reg = reader.read_u3()?.into(); - let idx = reader.read_u5()?.into(); - let step = reader.read_i8()?.into(); - Self::Stp(reg, idx, step) - } - INSTR_REM => { - let reg1 = reader.read_u3()?.into(); - let src1 = reader.read_u5()?.into(); - let reg2 = reader.read_u3()?.into(); - let src2 = reader.read_u5()?.into(); - Self::Rem(reg1, src1, reg2, src2) - } - INSTR_ABS => Self::Abs(reader.read_u4()?.into(), reader.read_u4()?.into()), - x => unreachable!("instruction {:#010b} classified as arithmetic operation", x), - } - }) - } -} - -impl Bytecode for BitwiseOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_AND..=INSTR_REVR } - - fn instr_byte(&self) -> u8 { - match self { - BitwiseOp::And(_, _, _, _) => INSTR_AND, - BitwiseOp::Or(_, _, _, _) => INSTR_OR, - BitwiseOp::Xor(_, _, _, _) => INSTR_XOR, - BitwiseOp::Not(_, _) => INSTR_NOT, - - BitwiseOp::Shl(_, _, _, _) => INSTR_SHF, - BitwiseOp::ShrA(_, _, _, _, _) => INSTR_SHF, - BitwiseOp::ShrR(_, _, _, _) => INSTR_SHF, - BitwiseOp::Scl(_, _, _, _) => INSTR_SHC, - BitwiseOp::Scr(_, _, _, _) => INSTR_SHC, - - BitwiseOp::RevA(_, _) => INSTR_REVA, - BitwiseOp::RevR(_, _) => INSTR_REVR, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - BitwiseOp::And(reg, idx1, idx2, idx3) - | BitwiseOp::Or(reg, idx1, idx2, idx3) - | BitwiseOp::Xor(reg, idx1, idx2, idx3) => { - writer.write_u4(reg)?; - writer.write_u4(idx1)?; - writer.write_u4(idx2)?; - writer.write_u4(idx3)?; - } - BitwiseOp::Not(reg, idx) => { - writer.write_u4(reg)?; - writer.write_u4(idx)?; - } - - BitwiseOp::Shl(a2, shift, reg, idx) => { - writer.write_u1(u1::with(0b0))?; - writer.write_u1(a2)?; - writer.write_u5(shift)?; - writer.write_u4(reg)?; - writer.write_u5(idx)?; - } - BitwiseOp::ShrA(sign, a2, shift, reg, idx) => { - writer.write_u1(u1::with(0b1))?; - writer.write_u1(a2)?; - writer.write_u4(shift)?; - writer.write_u1(sign)?; - writer.write_u1(u1::with(0b0))?; - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - BitwiseOp::ShrR(a2, shift, reg, idx) => { - writer.write_u1(u1::with(0b1))?; - writer.write_u1(a2)?; - writer.write_u5(shift)?; - writer.write_u1(u1::with(0b1))?; - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - - BitwiseOp::Scl(a2, shift, reg, idx) => { - writer.write_u1(u1::with(0b0))?; - writer.write_u1(a2)?; - writer.write_u5(shift)?; - writer.write_u4(reg)?; - writer.write_u5(idx)?; - } - BitwiseOp::Scr(a2, shift, reg, idx) => { - writer.write_u1(u1::with(0b1))?; - writer.write_u1(a2)?; - writer.write_u5(shift)?; - writer.write_u4(reg)?; - writer.write_u5(idx)?; - } - - BitwiseOp::RevA(reg, idx) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - BitwiseOp::RevR(reg, idx) => { - writer.write_u3(reg)?; - writer.write_u5(idx)?; - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - let instr = reader.read_u8()?; - - Ok(if (INSTR_AND..=INSTR_XOR).contains(&instr) { - let reg = reader.read_u4()?.into(); - let src1 = reader.read_u4()?.into(); - let src2 = reader.read_u4()?.into(); - let dst = reader.read_u4()?.into(); - match instr { - INSTR_AND => Self::And(reg, src1, src2, dst), - INSTR_OR => Self::Or(reg, src1, src2, dst), - INSTR_XOR => Self::Xor(reg, src1, src2, dst), - _ => unreachable!(), - } - } else if instr == INSTR_SHC { - let code = reader.read_u1()?; - let a2 = reader.read_u1()?.into(); - let shift = reader.read_u5()?.into(); - let reg = reader.read_u4()?.into(); - let idx = reader.read_u5()?.into(); - match code.into_u8() { - 0b0 => Self::Scl(a2, shift, reg, idx), - 0b1 => Self::Scr(a2, shift, reg, idx), - _ => unreachable!(), - } - } else if instr == INSTR_SHF { - let code = reader.read_u1()?; - let a2 = reader.read_u1()?.into(); - match code.into_u8() { - 0b0 => { - let shift = reader.read_u5()?; - let reg = reader.read_u4()?; - let idx = reader.read_u5()?.into(); - Self::Shl(a2, shift.into(), reg.into(), idx) - } - 0b1 => { - let shift = reader.read_u4()?; - let sign = reader.read_u1()?; - let block = reader.read_u1()?; - let reg = reader.read_u3()?; - let idx = reader.read_u5()?.into(); - let shift2 = u5::with(sign.into_u8() << 4 | shift.to_u8()).into(); - match block.into_u8() { - 0b0 => Self::ShrA(sign.into(), a2, shift.into(), reg.into(), idx), - 0b1 => Self::ShrR(a2, shift2, reg.into(), idx), - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } else { - match instr { - INSTR_NOT => Self::Not(reader.read_u4()?.into(), reader.read_u4()?.into()), - INSTR_REVA => Self::RevA(reader.read_u3()?.into(), reader.read_u5()?.into()), - INSTR_REVR => Self::RevR(reader.read_u3()?.into(), reader.read_u5()?.into()), - x => unreachable!("instruction {:#010b} classified as bitwise operation", x), - } - }) - } -} - -impl Bytecode for BytesOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_PUT..=INSTR_REV } - - fn instr_byte(&self) -> u8 { - match self { - BytesOp::Put(_, _, _) => INSTR_PUT, - BytesOp::Mov(_, _) => INSTR_MVS, - BytesOp::Swp(_, _) => INSTR_SWP, - BytesOp::Fill(_, _, _, _, _) => INSTR_FILL, - BytesOp::Len(_, _, _) => INSTR_LEN, - BytesOp::Cnt(_, _, _) => INSTR_CNT, - BytesOp::Eq(_, _) => INSTR_EQ, - BytesOp::Con(_, _, _, _, _) => INSTR_CON, - BytesOp::Find(_, _) => INSTR_FIND, - BytesOp::Extr(_, _, _, _) => INSTR_EXTR, - BytesOp::Inj(_, _, _, _) => INSTR_INJ, - BytesOp::Join(_, _, _) => INSTR_JOIN, - BytesOp::Splt(_, _, _, _, _) => INSTR_SPLT, - BytesOp::Ins(_, _, _, _) => INSTR_INS, - BytesOp::Del(_, _, _, _, _, _, _, _, _) => INSTR_DEL, - BytesOp::Rev(_, _) => INSTR_REV, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - BytesOp::Put(reg, bytes, _) => { - writer.write_u8(reg)?; - writer.write_data(bytes.as_ref())?; - } - BytesOp::Mov(reg1, reg2) - | BytesOp::Swp(reg1, reg2) - | BytesOp::Find(reg1, reg2) - | BytesOp::Rev(reg1, reg2) => { - writer.write_u4(reg1)?; - writer.write_u4(reg2)?; - } - BytesOp::Fill(reg, offset1, offset2, value, flag) => { - writer.write_u8(reg)?; - writer.write_u5(offset1)?; - writer.write_u5(offset2)?; - writer.write_u5(value)?; - writer.write_u1(flag)?; - } - BytesOp::Len(src, reg, dst) => { - writer.write_u8(src)?; - writer.write_u3(reg)?; - writer.write_u5(dst)?; - } - BytesOp::Cnt(src, byte, cnt) => { - writer.write_u8(src)?; - writer.write_u4(byte)?; - writer.write_u4(cnt)?; - } - BytesOp::Eq(reg1, reg2) => { - writer.write_u4(reg1)?; - writer.write_u4(reg2)?; - } - BytesOp::Con(reg1, reg2, no, offset, len) => { - writer.write_u4(reg1)?; - writer.write_u4(reg2)?; - writer.write_u5(no)?; - writer.write_u5(offset)?; - writer.write_u5(len)?; - writer.write_bool(false)?; - } - BytesOp::Extr(src, dst, index, offset) | BytesOp::Inj(src, dst, index, offset) => { - writer.write_u4(src)?; - writer.write_u4(dst)?; - writer.write_u4(index)?; - writer.write_u4(offset)?; - } - BytesOp::Join(src1, src2, dst) => { - writer.write_u4(src1)?; - writer.write_u4(src2)?; - writer.write_u8(dst)?; - } - BytesOp::Splt(flag, offset, src, dst1, dst2) => { - writer.write_u3(flag)?; - writer.write_u5(offset)?; - writer.write_u8(src)?; - writer.write_u4(dst1)?; - writer.write_u4(dst2)?; - } - BytesOp::Ins(flag, offset, src, dst) => { - writer.write_u3(flag)?; - writer.write_u5(offset)?; - writer.write_u4(src)?; - writer.write_u4(dst)?; - } - BytesOp::Del(flag, reg1, offset1, reg2, offset2, flag1, flag2, src, dst) => { - writer.write_u2(flag)?; - writer.write_u1(reg1)?; - writer.write_u5(offset1)?; - writer.write_u1(reg2)?; - writer.write_u5(offset2)?; - writer.write_bool(*flag1)?; - writer.write_bool(*flag2)?; - writer.write_u4(src)?; - writer.write_u4(dst)?; - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - Ok(match reader.read_u8()? { - INSTR_PUT => { - let index = reader.read_u8()?; - let (data, st0) = reader.read_data()?; - Self::Put(index.into(), Box::new(ByteStr::with(data)), st0) - } - INSTR_MVS => Self::Mov(reader.read_u4()?.into(), reader.read_u4()?.into()), - INSTR_SWP => Self::Swp(reader.read_u4()?.into(), reader.read_u4()?.into()), - INSTR_FIND => Self::Find(reader.read_u4()?.into(), reader.read_u4()?.into()), - INSTR_REV => Self::Rev(reader.read_u4()?.into(), reader.read_u4()?.into()), - - INSTR_FILL => Self::Fill( - reader.read_u8()?.into(), - reader.read_u5()?.into(), - reader.read_u5()?.into(), - reader.read_u5()?.into(), - reader.read_u1()?.into(), - ), - INSTR_LEN => Self::Len( - reader.read_u8()?.into(), - reader.read_u3()?.into(), - reader.read_u5()?.into(), - ), - INSTR_CNT => Self::Cnt( - reader.read_u8()?.into(), - reader.read_u4()?.into(), - reader.read_u4()?.into(), - ), - INSTR_EQ => Self::Eq(reader.read_u4()?.into(), reader.read_u4()?.into()), - INSTR_CON => { - let op = Self::Con( - reader.read_u4()?.into(), - reader.read_u4()?.into(), - reader.read_u5()?.into(), - reader.read_u5()?.into(), - reader.read_u5()?.into(), - ); - let _ = reader.read_bool()?; - op - } - INSTR_EXTR => Self::Extr( - reader.read_u4()?.into(), - reader.read_u4()?.into(), - reader.read_u4()?.into(), - reader.read_u4()?.into(), - ), - INSTR_INJ => Self::Inj( - reader.read_u4()?.into(), - reader.read_u4()?.into(), - reader.read_u4()?.into(), - reader.read_u4()?.into(), - ), - INSTR_JOIN => Self::Join( - reader.read_u4()?.into(), - reader.read_u4()?.into(), - reader.read_u8()?.into(), - ), - INSTR_SPLT => Self::Splt( - reader.read_u3()?.into(), - reader.read_u5()?.into(), - reader.read_u8()?.into(), - reader.read_u4()?.into(), - reader.read_u4()?.into(), - ), - INSTR_INS => Self::Ins( - reader.read_u3()?.into(), - reader.read_u5()?.into(), - reader.read_u4()?.into(), - reader.read_u4()?.into(), - ), - INSTR_DEL => Self::Del( - reader.read_u2()?.into(), - reader.read_u1()?.into(), - reader.read_u5()?.into(), - reader.read_u1()?.into(), - reader.read_u5()?.into(), - reader.read_bool()?, - reader.read_bool()?, - reader.read_u4()?.into(), - reader.read_u4()?.into(), - ), - x => unreachable!("instruction {:#010b} classified as byte string operation", x), - }) - } -} - -impl Bytecode for DigestOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_RIPEMD..=INSTR_SHA512 } - - fn instr_byte(&self) -> u8 { - match self { - DigestOp::Ripemd(_, _) => INSTR_RIPEMD, - DigestOp::Sha256(_, _) => INSTR_SHA256, - DigestOp::Sha512(_, _) => INSTR_SHA512, - DigestOp::Blake3(_, _) => INSTR_BLAKE3, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - DigestOp::Ripemd(src, dst) - | DigestOp::Sha256(src, dst) - | DigestOp::Blake3(src, dst) - | DigestOp::Sha512(src, dst) => { - writer.write_u4(src)?; - writer.write_u4(dst)?; - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - let instr = reader.read_u8()?; - let src = reader.read_u4()?.into(); - let dst = reader.read_u4()?.into(); - - Ok(match instr { - INSTR_RIPEMD => Self::Ripemd(src, dst), - INSTR_SHA256 => Self::Sha256(src, dst), - INSTR_BLAKE3 => Self::Blake3(src, dst), - INSTR_SHA512 => Self::Sha512(src, dst), - x => unreachable!("instruction {:#010b} classified as digest operation", x), - }) - } + /// Reads an instruction operands from bytecode, provided the opcode byte. + fn decode_operands(reader: &mut R, opcode: u8) -> Result + where + Self: Sized, + R: BytecodeRead; } -impl Bytecode for Secp256k1Op { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_SECP_GEN..=INSTR_SECP_NEG } - - fn instr_byte(&self) -> u8 { - match self { - Secp256k1Op::Gen(_, _) => INSTR_SECP_GEN, - Secp256k1Op::Mul(_, _, _, _) => INSTR_SECP_MUL, - Secp256k1Op::Add(_, _) => INSTR_SECP_ADD, - Secp256k1Op::Neg(_, _) => INSTR_SECP_NEG, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - Secp256k1Op::Gen(src, dst) => { - writer.write_u5(src)?; - writer.write_u3(dst)?; - } - Secp256k1Op::Mul(reg, scal, src, dst) => { - writer.write_bool(*reg == RegBlockAR::A)?; - writer.write_u5(scal)?; - writer.write_u5(src)?; - writer.write_u5(dst)?; - } - Secp256k1Op::Add(src, srcdst) => { - writer.write_u5(src)?; - writer.write_u3(srcdst)?; - } - Secp256k1Op::Neg(src, dst) => { - writer.write_u5(src)?; - writer.write_u3(dst)?; - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - Ok(match reader.read_u8()? { - INSTR_SECP_GEN => Self::Gen(reader.read_u5()?.into(), reader.read_u3()?.into()), - INSTR_SECP_MUL => Self::Mul( - if reader.read_bool()? { RegBlockAR::A } else { RegBlockAR::R }, - reader.read_u5()?.into(), - reader.read_u5()?.into(), - reader.read_u5()?.into(), - ), - INSTR_SECP_ADD => Self::Add(reader.read_u5()?.into(), reader.read_u3()?.into()), - INSTR_SECP_NEG => Self::Neg(reader.read_u5()?.into(), reader.read_u3()?.into()), - x => unreachable!("instruction {:#010b} classified as Secp256k1 curve operation", x), - }) - } +/// Error indicating that an end of code segment boundary is reached during read or write operation. +#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)] +#[display("attempt to read or write outside of code segment (i.e. at position > 0xFFFF)")] +pub struct CodeEofError; + +/// Reader from a bytecode for instruction deserialization. +pub trait BytecodeRead { + /// Return current byte offset of the cursor. Does not account for bits. + /// If the position is exactly at EOF, returns `None`. + fn pos(&self) -> u16; + /// Set current cursor byte offset to the provided value, if it is less than the underlying + /// buffer length. + /// + /// # Returns + /// + /// Previous position. + fn seek(&mut self, byte_pos: u16) -> Result; + /// Return whether end of the bytecode is reached. + fn is_eof(&self) -> bool; + /// Peek a single byte without moving cursor. + fn peek_byte(&self) -> Result; + + /// Read single bit as a bool value. + fn read_bool(&mut self) -> Result { Ok(self.read_1bit()? == u1::ONE) } + /// Read single bit. + fn read_1bit(&mut self) -> Result; + /// Read two bits. + fn read_2bits(&mut self) -> Result; + /// Read three bits. + fn read_3bits(&mut self) -> Result; + /// Read four bits. + fn read_4bits(&mut self) -> Result; + /// Read five bits. + fn read_5bits(&mut self) -> Result; + /// Read six bits. + fn read_6bits(&mut self) -> Result; + /// Read seven bits. + fn read_7bits(&mut self) -> Result; + + /// Read byte. + fn read_byte(&mut self) -> Result; + /// Read word. + fn read_word(&mut self) -> Result; + + /// Read fixed number of bytes and convert it into a result type. + /// + /// # Returns + /// + /// Resulting data type and a flag for `CK` registry indicating whether it was possible to read + /// all the data. + fn read_fixed( + &mut self, + f: impl FnOnce([u8; LEN]) -> N, + ) -> Result; + + /// Read variable-length byte string. + /// + /// # Returns + /// + /// Resulting data type and a flag for `CK` registry indicating whether it was possible to read + /// all the data. + fn read_bytes(&mut self) -> Result<(SmallBlob, bool), CodeEofError>; + + /// Read external reference id. + fn read_ref(&mut self) -> Result + where Id: Sized; + + /// Check if the current cursor position is aligned to the next byte. + /// + /// # Panics + /// + /// If the position is not aligned, panics. + fn check_aligned(&self); } -impl Bytecode for Curve25519Op { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_ED_GEN..=INSTR_ED_NEG } - - fn instr_byte(&self) -> u8 { - match self { - Curve25519Op::Gen(_, _) => INSTR_ED_GEN, - Curve25519Op::Mul(_, _, _, _) => INSTR_ED_MUL, - Curve25519Op::Add(_, _, _, _) => INSTR_ED_ADD, - Curve25519Op::Neg(_, _) => INSTR_ED_NEG, - } - } - - fn encode_args(&self, writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - Curve25519Op::Gen(src, dst) => { - writer.write_u5(src)?; - writer.write_u3(dst)?; - } - Curve25519Op::Mul(reg, scal, src, dst) => { - writer.write_bool(*reg == RegBlockAR::A)?; - writer.write_u5(scal)?; - writer.write_u5(src)?; - writer.write_u5(dst)?; - } - Curve25519Op::Add(src1, src2, dst, overflow) => { - writer.write_u5(src1)?; - writer.write_u5(src2)?; - writer.write_u5(dst)?; - writer.write_bool(*overflow)?; - } - Curve25519Op::Neg(src, dst) => { - writer.write_u5(src)?; - writer.write_u3(dst)?; - } - } - Ok(()) - } - - fn decode(reader: &mut R) -> Result - where R: Read { - Ok(match reader.read_u8()? { - INSTR_ED_GEN => Self::Gen(reader.read_u5()?.into(), reader.read_u3()?.into()), - INSTR_ED_MUL => Self::Mul( - if reader.read_bool()? { RegBlockAR::A } else { RegBlockAR::R }, - reader.read_u5()?.into(), - reader.read_u5()?.into(), - reader.read_u5()?.into(), - ), - INSTR_ED_ADD => Self::Add( - reader.read_u5()?.into(), - reader.read_u5()?.into(), - reader.read_u5()?.into(), - reader.read_bool()?, - ), - INSTR_ED_NEG => Self::Neg(reader.read_u5()?.into(), reader.read_u3()?.into()), - x => unreachable!("instruction {:#010b} classified as Curve25519 operation", x), - }) - } -} - -impl Bytecode for ReservedOp { - #[inline] - fn instr_range() -> RangeInclusive { INSTR_RESV_FROM..=INSTR_ISAE_TO } - - #[inline] - fn instr_byte(&self) -> u8 { self.0 } - - #[inline] - fn encode_args(&self, _writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - Ok(()) - } - - #[inline] - fn decode(reader: &mut R) -> Result - where R: Read { - Ok(ReservedOp(reader.read_u8()?)) - } +/// Writer converting instructions into a bytecode. +pub trait BytecodeWrite { + type Error: Debug; + + /// Write a single bit from a bool value. + fn write_bool(&mut self, data: bool) -> Result<(), Self::Error> { + self.write_1bit(if data { u1::ONE } else { u1::ZERO }) + } + + /// Write a single bit. + fn write_1bit(&mut self, data: impl Into) -> Result<(), Self::Error>; + /// Write two bits. + fn write_2bits(&mut self, data: impl Into) -> Result<(), Self::Error>; + /// Write three bits. + fn write_3bits(&mut self, data: impl Into) -> Result<(), Self::Error>; + /// Write four bits. + fn write_4bits(&mut self, data: impl Into) -> Result<(), Self::Error>; + /// Write five bits. + fn write_5bits(&mut self, data: impl Into) -> Result<(), Self::Error>; + /// Write six bits. + fn write_6bits(&mut self, data: impl Into) -> Result<(), Self::Error>; + /// Write seven bits. + fn write_7bits(&mut self, data: impl Into) -> Result<(), Self::Error>; + + /// Write byte. + fn write_byte(&mut self, data: u8) -> Result<(), Self::Error>; + /// Write word. + fn write_word(&mut self, data: u16) -> Result<(), Self::Error>; + + /// Write data representable as a fixed-length byte array. + fn write_fixed(&mut self, data: [u8; LEN]) -> Result<(), Self::Error>; + + /// Write variable-length byte string. + fn write_bytes(&mut self, data: &[u8]) -> Result<(), Self::Error>; + + /// Write external reference id. + fn write_ref(&mut self, id: Id) -> Result<(), Self::Error>; + + /// Check if the current cursor position is aligned to the next byte. + /// + /// # Panics + /// + /// If the position is not aligned, panics. + fn check_aligned(&self); } diff --git a/src/isa/ctrl/bytecode.rs b/src/isa/ctrl/bytecode.rs new file mode 100644 index 0000000..3da0a9b --- /dev/null +++ b/src/isa/ctrl/bytecode.rs @@ -0,0 +1,283 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use core::ops::RangeInclusive; + +use super::CtrlInstr; +use crate::core::SiteId; +use crate::isa::bytecode::CodeEofError; +use crate::isa::{Bytecode, BytecodeRead, BytecodeWrite, Instr, ReservedInstr}; +use crate::Site; + +impl Bytecode for Instr { + fn op_range() -> RangeInclusive { 0..=0xFF } + + fn opcode_byte(&self) -> u8 { + match self { + Instr::Ctrl(instr) => instr.opcode_byte(), + Instr::Reserved(instr) => Bytecode::::opcode_byte(instr), + } + } + + fn encode_operands(&self, writer: &mut W) -> Result<(), W::Error> + where W: BytecodeWrite { + match self { + Instr::Ctrl(instr) => instr.encode_operands(writer), + Instr::Reserved(instr) => instr.encode_operands(writer), + } + } + + fn decode_operands(reader: &mut R, opcode: u8) -> Result + where + Self: Sized, + R: BytecodeRead, + { + match opcode { + op if CtrlInstr::::op_range().contains(&op) => { + CtrlInstr::::decode_operands(reader, op).map(Self::Ctrl) + } + _ => ReservedInstr::decode_operands(reader, opcode).map(Self::Reserved), + } + } +} + +impl Bytecode for ReservedInstr { + fn op_range() -> RangeInclusive { 0..=0x7F } + + fn opcode_byte(&self) -> u8 { self.0 } + + fn encode_operands(&self, _writer: &mut W) -> Result<(), W::Error> + where W: BytecodeWrite { + Ok(()) + } + + fn decode_operands(_reader: &mut R, opcode: u8) -> Result + where + Self: Sized, + R: BytecodeRead, + { + Ok(ReservedInstr(opcode)) + } +} + +impl CtrlInstr { + const START: u8 = 0; + const END: u8 = Self::START + Self::STOP; + + const NOP: u8 = 0; + const NOCO: u8 = 1; + const CHK: u8 = 2; + const FAIL: u8 = 3; + const RSET: u8 = 4; + const JMP: u8 = 5; + const JINE: u8 = 6; + const JIFAIL: u8 = 7; + const SH: u8 = 8; + const SHNE: u8 = 9; + const SHFAIL: u8 = 10; + const EXEC: u8 = 11; + const FN: u8 = 12; + const CALL: u8 = 13; + const RET: u8 = 14; + const STOP: u8 = 15; +} + +impl Bytecode for CtrlInstr { + fn op_range() -> RangeInclusive { Self::START..=Self::END } + + fn opcode_byte(&self) -> u8 { + match self { + CtrlInstr::Nop => Self::NOP, + CtrlInstr::Chk => Self::CHK, + CtrlInstr::NotCo => Self::NOCO, + CtrlInstr::FailCk => Self::FAIL, + CtrlInstr::RsetCk => Self::RSET, + CtrlInstr::Jmp { .. } => Self::JMP, + CtrlInstr::JiNe { .. } => Self::JINE, + CtrlInstr::JiFail { .. } => Self::JIFAIL, + CtrlInstr::Sh { .. } => Self::SH, + CtrlInstr::ShNe { .. } => Self::SHNE, + CtrlInstr::ShFail { .. } => Self::SHFAIL, + CtrlInstr::Exec { .. } => Self::EXEC, + CtrlInstr::Fn { .. } => Self::FN, + CtrlInstr::Call { .. } => Self::CALL, + CtrlInstr::Ret => Self::RET, + CtrlInstr::Stop => Self::STOP, + } + } + + fn encode_operands(&self, writer: &mut W) -> Result<(), W::Error> + where W: BytecodeWrite { + match *self { + CtrlInstr::Nop + | CtrlInstr::Chk + | CtrlInstr::FailCk + | CtrlInstr::RsetCk + | CtrlInstr::NotCo + | CtrlInstr::Ret + | CtrlInstr::Stop => {} + + CtrlInstr::Jmp { pos } + | CtrlInstr::JiNe { pos } + | CtrlInstr::JiFail { pos } + | CtrlInstr::Fn { pos } => writer.write_word(pos)?, + CtrlInstr::Sh { shift } | CtrlInstr::ShNe { shift } | CtrlInstr::ShFail { shift } => { + writer.write_byte(shift.to_le_bytes()[0])? + } + CtrlInstr::Call { site } | CtrlInstr::Exec { site } => { + let site = Site::new(site.prog_id, site.offset); + writer.write_ref(site.prog_id)?; + writer.write_word(site.offset)?; + } + } + Ok(()) + } + + fn decode_operands(reader: &mut R, opcode: u8) -> Result + where + Self: Sized, + R: BytecodeRead, + { + Ok(match opcode { + Self::NOP => Self::Nop, + Self::CHK => Self::Chk, + Self::FAIL => Self::FailCk, + Self::RSET => Self::RsetCk, + Self::NOCO => Self::NotCo, + Self::RET => Self::Ret, + Self::STOP => Self::Stop, + + Self::JMP => CtrlInstr::Jmp { pos: reader.read_word()? }, + Self::JINE => CtrlInstr::JiNe { pos: reader.read_word()? }, + Self::JIFAIL => CtrlInstr::JiFail { pos: reader.read_word()? }, + Self::FN => CtrlInstr::Fn { pos: reader.read_word()? }, + + Self::SH => CtrlInstr::Sh { shift: i8::from_le_bytes([reader.read_byte()?]) }, + Self::SHNE => CtrlInstr::ShNe { shift: i8::from_le_bytes([reader.read_byte()?]) }, + Self::SHFAIL => CtrlInstr::ShFail { shift: i8::from_le_bytes([reader.read_byte()?]) }, + + Self::CALL => { + let prog_id = reader.read_ref()?; + let offset = reader.read_word()?; + let site = Site::new(prog_id, offset); + CtrlInstr::Call { site } + } + Self::EXEC => { + let prog_id = reader.read_ref()?; + let offset = reader.read_word()?; + let site = Site::new(prog_id, offset); + CtrlInstr::Exec { site } + } + + _ => unreachable!(), + }) + } +} + +#[cfg(test)] +mod test { + use core::str::FromStr; + + use amplify::confinement::SmallBlob; + + use super::*; + use crate::library::{LibId, LibsSeg, Marshaller}; + + const LIB_ID: &str = "5iMb1eHJ-bN5BOe6-9RvBjYL-jF1ELjj-VV7c8Bm-WvFen1Q"; + + fn roundtrip(instr: impl Into>, bytecode: impl AsRef<[u8]>) -> SmallBlob { + let instr = instr.into(); + let mut libs = LibsSeg::new(); + libs.push(LibId::from_str(LIB_ID).unwrap()).unwrap(); + let mut marshaller = Marshaller::new(&libs); + instr.encode_instr(&mut marshaller).unwrap(); + let (code, data) = marshaller.finish(); + assert_eq!(code.as_slice(), bytecode.as_ref()); + let mut marshaller = Marshaller::with(code, data, &libs); + let decoded = Instr::::decode_instr(&mut marshaller).unwrap(); + assert_eq!(decoded, instr); + marshaller.into_code_data().1 + } + + #[test] + fn nop() { roundtrip(CtrlInstr::Nop, [CtrlInstr::::NOP]); } + #[test] + fn chk() { roundtrip(CtrlInstr::Chk, [CtrlInstr::::CHK]); } + #[test] + fn not_co() { roundtrip(CtrlInstr::NotCo, [CtrlInstr::::NOCO]); } + #[test] + fn fail_ck() { roundtrip(CtrlInstr::FailCk, [CtrlInstr::::FAIL]); } + #[test] + fn reset_ck() { roundtrip(CtrlInstr::RsetCk, [CtrlInstr::::RSET]); } + + #[test] + fn jmp() { roundtrip(CtrlInstr::Jmp { pos: 0x75AE }, [CtrlInstr::::JMP, 0xAE, 0x75]); } + #[test] + fn jine() { + roundtrip(CtrlInstr::JiNe { pos: 0x75AE }, [CtrlInstr::::JINE, 0xAE, 0x75]); + } + #[test] + fn jifail() { + roundtrip(CtrlInstr::JiFail { pos: 0x75AE }, [CtrlInstr::::JIFAIL, 0xAE, 0x75]); + } + + #[test] + fn sh() { roundtrip(CtrlInstr::Sh { shift: -0x5 }, [CtrlInstr::::SH, 255 - 5 + 1]); } + #[test] + fn shne() { + roundtrip(CtrlInstr::ShNe { shift: -0x5 }, [CtrlInstr::::SHNE, 255 - 5 + 1]); + } + #[test] + fn shfail() { + roundtrip(CtrlInstr::ShFail { shift: -0x5 }, [CtrlInstr::::SHFAIL, 255 - 5 + 1]); + } + + #[test] + fn exec() { + let lib_id = LibId::from_str(LIB_ID).unwrap(); + roundtrip(CtrlInstr::Exec { site: Site::new(lib_id, 0x69AB) }, [ + CtrlInstr::::EXEC, + 0x00, + 0xAB, + 0x69, + ]); + } + #[test] + fn func() { roundtrip(CtrlInstr::Fn { pos: 0x75AE }, [CtrlInstr::::FN, 0xAE, 0x75]); } + #[test] + fn call() { + let lib_id = LibId::from_str(LIB_ID).unwrap(); + roundtrip(CtrlInstr::Call { site: Site::new(lib_id, 0x69AB) }, [ + CtrlInstr::::CALL, + 0x00, + 0xAB, + 0x69, + ]); + } + + #[test] + fn ret() { roundtrip(CtrlInstr::Ret, [CtrlInstr::::RET]); } + #[test] + fn stop() { roundtrip(CtrlInstr::Stop, [CtrlInstr::::STOP]); } +} diff --git a/src/isa/ctrl/exec.rs b/src/isa/ctrl/exec.rs new file mode 100644 index 0000000..1a2a1f6 --- /dev/null +++ b/src/isa/ctrl/exec.rs @@ -0,0 +1,223 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use alloc::collections::BTreeSet; + +use super::CtrlInstr; +use crate::core::{Core, NoExt, NoRegs, Site, SiteId, Status}; +use crate::isa::{ExecStep, Instr, Instruction, ReservedInstr}; + +impl Instruction for Instr { + const ISA_EXT: &'static [&'static str] = &[]; + + type Core = NoExt; + type Context<'ctx> = (); + + fn src_regs(&self) -> BTreeSet { + match self { + Instr::Ctrl(instr) => instr.src_regs(), + Instr::Reserved(instr) => Instruction::::src_regs(instr), + } + } + + fn dst_regs(&self) -> BTreeSet { + match self { + Instr::Ctrl(instr) => instr.dst_regs(), + Instr::Reserved(instr) => Instruction::::dst_regs(instr), + } + } + + fn op_data_bytes(&self) -> u16 { + match self { + Instr::Ctrl(instr) => instr.op_data_bytes(), + Instr::Reserved(instr) => Instruction::::op_data_bytes(instr), + } + } + + fn ext_data_bytes(&self) -> u16 { + match self { + Instr::Ctrl(instr) => instr.ext_data_bytes(), + Instr::Reserved(instr) => Instruction::::ext_data_bytes(instr), + } + } + + fn exec( + &self, + site: Site, + core: &mut Core, + _: &Self::Context<'_>, + ) -> ExecStep> { + match self { + Instr::Ctrl(instr) => instr.exec(site, core, &()), + Instr::Reserved(instr) => instr.exec(site, core, &()), + } + } +} + +impl Instruction for ReservedInstr { + const ISA_EXT: &'static [&'static str] = &[]; + + type Core = NoExt; + type Context<'ctx> = (); + + fn src_regs(&self) -> BTreeSet { none!() } + + fn dst_regs(&self) -> BTreeSet { none!() } + + fn op_data_bytes(&self) -> u16 { none!() } + + fn ext_data_bytes(&self) -> u16 { none!() } + + fn complexity(&self) -> u64 { u64::MAX } + + fn exec( + &self, + _: Site, + _: &mut Core, + _: &Self::Context<'_>, + ) -> ExecStep> { + ExecStep::FailHalt + } +} + +impl Instruction for CtrlInstr { + const ISA_EXT: &'static [&'static str] = &[]; + + type Core = NoExt; + type Context<'ctx> = (); + + fn src_regs(&self) -> BTreeSet { none!() } + + fn dst_regs(&self) -> BTreeSet { none!() } + + fn op_data_bytes(&self) -> u16 { + match self { + CtrlInstr::Nop + | CtrlInstr::Chk + | CtrlInstr::NotCo + | CtrlInstr::FailCk + | CtrlInstr::RsetCk => 0, + CtrlInstr::Jmp { .. } | CtrlInstr::JiNe { .. } | CtrlInstr::JiFail { .. } => 2, + CtrlInstr::Sh { .. } | CtrlInstr::ShNe { .. } | CtrlInstr::ShFail { .. } => 1, + CtrlInstr::Exec { .. } => 2, + CtrlInstr::Fn { .. } => 2, + CtrlInstr::Call { .. } => 2, + CtrlInstr::Ret | CtrlInstr::Stop => 0, + } + } + + fn ext_data_bytes(&self) -> u16 { + match self { + CtrlInstr::Nop + | CtrlInstr::Chk + | CtrlInstr::NotCo + | CtrlInstr::FailCk + | CtrlInstr::RsetCk => 0, + CtrlInstr::Jmp { .. } | CtrlInstr::JiNe { .. } | CtrlInstr::JiFail { .. } => 0, + CtrlInstr::Sh { .. } | CtrlInstr::ShNe { .. } | CtrlInstr::ShFail { .. } => 0, + CtrlInstr::Exec { .. } => 32, + CtrlInstr::Fn { .. } => 0, + CtrlInstr::Call { .. } => 32, + CtrlInstr::Ret | CtrlInstr::Stop => 0, + } + } + + fn exec( + &self, + current: Site, + core: &mut Core, + _: &Self::Context<'_>, + ) -> ExecStep> { + let shift_jump = |shift: i8| { + let Some(pos) = current.offset.checked_add_signed(shift as i16) else { + return ExecStep::FailHalt; + }; + ExecStep::Jump(pos) + }; + + match *self { + CtrlInstr::Nop => {} + CtrlInstr::Chk => { + if core.ck() == Status::Fail { + return ExecStep::Stop; + } + } + CtrlInstr::FailCk => { + if core.fail_ck() { + return ExecStep::Stop; + } + } + CtrlInstr::RsetCk => { + core.set_co(core.ck() == Status::Fail); + core.reset_ck() + } + CtrlInstr::NotCo => core.set_co(!core.co()), + CtrlInstr::Jmp { pos } => return ExecStep::Jump(pos), + CtrlInstr::JiNe { pos } => { + if core.co() { + return ExecStep::Jump(pos); + } + } + CtrlInstr::JiFail { pos } => { + if core.ck() == Status::Fail { + return ExecStep::Jump(pos); + } + } + CtrlInstr::Sh { shift } => { + return shift_jump(shift); + } + CtrlInstr::ShNe { shift } => { + if core.co() { + return shift_jump(shift); + } + } + CtrlInstr::ShFail { shift } => { + if core.ck() == Status::Fail { + return shift_jump(shift); + } + } + CtrlInstr::Exec { site } => return ExecStep::Call(site), + CtrlInstr::Fn { pos } => { + return match core.push_cs(current) { + Some(_) => ExecStep::Jump(pos), + None => ExecStep::FailHalt, + } + } + CtrlInstr::Call { site } => { + return match core.push_cs(current) { + Some(_) => ExecStep::Call(site), + None => ExecStep::FailHalt, + } + } + CtrlInstr::Ret => { + return match core.pop_cs() { + Some(site) => ExecStep::Call(site), + None => ExecStep::Stop, + } + } + CtrlInstr::Stop => return ExecStep::Stop, + } + ExecStep::Next + } +} diff --git a/src/isa/ctrl/instr.rs b/src/isa/ctrl/instr.rs new file mode 100644 index 0000000..9d67652 --- /dev/null +++ b/src/isa/ctrl/instr.rs @@ -0,0 +1,95 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use crate::core::SiteId; +use crate::Site; + +/// Control flow instructions. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)] +#[display(inner)] +pub enum CtrlInstr { + /// Not an operation. + #[display("nop")] + Nop, + + /// Test `CK` value, terminates if in failed state. + #[display("chk")] + Chk, + + /// Invert `CO` register. + #[display("not CO")] + NotCo, + + /// Set `CK` register to a failed state. + #[display("put CK, :fail")] + FailCk, + + /// Reset `CK` register. + #[display("put CK, :ok")] + RsetCk, + + /// Jump to location (unconditionally). + #[display("jmp {pos:04X}#h")] + Jmp { pos: u16 }, + + /// Jump to location if `CO` is true. + #[display("jif CO, {pos:04X}#h")] + JiNe { pos: u16 }, + + /// Jump to location if `ck` is in a failed state. + #[display("jif CK, {pos:04X}#h")] + JiFail { pos: u16 }, + + /// Relative jump. + #[display("jmp {shift:+03X}#h")] + Sh { shift: i8 }, + + /// Relative jump if `CO` is true. + #[display("jif CO, {shift:+03X}#h")] + ShNe { shift: i8 }, + + /// Relative jump if `CK` is in a failed state. + #[display("jif CK, {shift:+03X}#h")] + ShFail { shift: i8 }, + + /// External jump. + #[display("jmp {site}")] + Exec { site: Site }, + + /// Subroutine call. + #[display("call {pos:04X}#h")] + Fn { pos: u16 }, + + /// External subroutine call. + #[display("call {site}")] + Call { site: Site }, + + /// Return from a subroutine or finish program. + #[display("ret")] + Ret, + + /// Stop the program. + #[display("stop")] + Stop, +} diff --git a/src/isa/ctrl/mod.rs b/src/isa/ctrl/mod.rs new file mode 100644 index 0000000..7477749 --- /dev/null +++ b/src/isa/ctrl/mod.rs @@ -0,0 +1,31 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +//! ALU64 instruction set architecture. + +mod bytecode; +mod instr; +mod exec; + +pub use instr::CtrlInstr; diff --git a/src/isa/exec.rs b/src/isa/exec.rs deleted file mode 100644 index d25bf5f..0000000 --- a/src/isa/exec.rs +++ /dev/null @@ -1,2124 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -use alloc::collections::BTreeSet; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -use core::cmp::Ordering; -use core::ops::{BitAnd, BitOr, BitXor, Neg, Rem, Shl, Shr}; - -use sha2::Digest; - -use super::{ - ArithmeticOp, BitwiseOp, Bytecode, BytesOp, CmpOp, ControlFlowOp, Curve25519Op, DigestOp, - Instr, MoveOp, PutOp, ReservedOp, Secp256k1Op, -}; -use crate::data::{ByteStr, MaybeNumber, Number, NumberLayout}; -use crate::isa::{ExtendFlag, FloatEqFlag, IntFlags, MergeFlag, NoneEqFlag, SignFlag}; -use crate::library::{constants, IsaName, IsaSeg, LibSite}; -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)] -pub enum ExecStep { - /// Stop program execution - Stop, - - /// Stop and fail program execution - Fail, - - /// Move to the next instruction - Next, - - /// Jump to the offset from the origin - Jump(u16), - - /// Jump to another code fragment - Call(LibSite), -} - -/// Trait for instructions -pub trait InstructionSet: Bytecode + core::fmt::Display + core::fmt::Debug { - /// Context: external data which are accessible to the ISA. - type Context<'ctx>; - - /// ISA Extensions used by the provided instruction set. - /// - /// Each id must be up to 8 bytes and consist of upper case latin alphanumeric characters, - /// starting with non-number. - fn isa_ids() -> IsaSeg; - - /// ISA Extension IDs represented as a standard string (space-separated) - /// - /// Concatenated length of the ISA IDs joined via ' ' character must not exceed 128 bytes. - #[inline] - fn isa_string() -> String { Self::isa_ids().to_string() } - - /// ISA Extension IDs encoded in a standard way (space-separated) - /// - /// Concatenated length of the ISA IDs joined via ' ' character must not exceed 128 bytes. - #[inline] - fn isa_id() -> Box<[u8]> { Self::isa_string().as_bytes().into() } - - /// Checks whether provided ISA extension ID is supported by the current instruction set - #[inline] - fn is_supported(id: &IsaName) -> bool { Self::isa_ids().contains(id) } - - /// Lists all registers which are used by the instruction. - fn regs(&self) -> BTreeSet { - 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) -> BTreeSet; - - /// List of registers which value may be changed by the instruction. - fn dst_regs(&self) -> BTreeSet; - - /// Returns computational complexity of the instruction - fn complexity(&self) -> u64; - - /// Executes given instruction taking all registers as input and output. - /// - /// # Arguments - /// - /// The method is provided with the current code position which may be used by the instruction - /// for constructing call stack. - /// - /// # Returns - /// - /// Returns whether further execution should be stopped. - // TODO: Take the instruction by reference - fn exec(&self, regs: &mut CoreRegs, site: LibSite, context: &Self::Context<'_>) -> ExecStep; -} - -impl InstructionSet for Instr -where Extension: InstructionSet -{ - type Context<'ctx> = Extension::Context<'ctx>; - - #[inline] - fn isa_ids() -> IsaSeg { - let mut set = IsaSeg::with(constants::ISA_ID_ALU); - set.extend(DigestOp::isa_ids()).expect("hardcoded"); - set.extend(Secp256k1Op::isa_ids()).expect("hardcoded"); - set.extend(Curve25519Op::isa_ids()).expect("hardcoded"); - set.extend(Extension::isa_ids()).expect("hardcoded"); - set - } - - fn src_regs(&self) -> BTreeSet { - 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 => BTreeSet::new(), - } - } - - fn dst_regs(&self) -> BTreeSet { - 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 => BTreeSet::new(), - } - } - - fn complexity(&self) -> u64 { - match self { - Instr::ControlFlow(instr) => instr.complexity(), - Instr::Put(instr) => instr.complexity(), - Instr::Move(instr) => instr.complexity(), - Instr::Cmp(instr) => instr.complexity(), - Instr::Arithmetic(instr) => instr.complexity(), - Instr::Bitwise(instr) => instr.complexity(), - Instr::Bytes(instr) => instr.complexity(), - Instr::Digest(instr) => instr.complexity(), - #[cfg(feature = "secp256k1")] - Instr::Secp256k1(instr) => instr.complexity(), - #[cfg(feature = "curve25519")] - Instr::Curve25519(instr) => instr.complexity(), - Instr::ExtensionCodes(instr) => instr.complexity(), - Instr::ReservedInstruction(instr) => instr.complexity(), - Instr::Nop => 1, - } - } - - #[inline] - fn exec(&self, regs: &mut CoreRegs, site: LibSite, ctx: &Self::Context<'_>) -> ExecStep { - match self { - Instr::ControlFlow(instr) => instr.exec(regs, site, &()), - Instr::Put(instr) => instr.exec(regs, site, &()), - Instr::Move(instr) => instr.exec(regs, site, &()), - Instr::Cmp(instr) => instr.exec(regs, site, &()), - Instr::Arithmetic(instr) => instr.exec(regs, site, &()), - Instr::Bitwise(instr) => instr.exec(regs, site, &()), - Instr::Bytes(instr) => instr.exec(regs, site, &()), - Instr::Digest(instr) => instr.exec(regs, site, &()), - #[cfg(feature = "secp256k1")] - Instr::Secp256k1(instr) => instr.exec(regs, site, &()), - #[cfg(feature = "curve25519")] - Instr::Curve25519(instr) => instr.exec(regs, site, &()), - Instr::ExtensionCodes(instr) => instr.exec(regs, site, ctx), - Instr::ReservedInstruction(_) => ControlFlowOp::Fail.exec(regs, site, &()), - Instr::Nop => ExecStep::Next, - } - } -} - -impl InstructionSet for ControlFlowOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { BTreeSet::new() } - - fn dst_regs(&self) -> BTreeSet { BTreeSet::new() } - - #[inline] - fn complexity(&self) -> u64 { 2 } - - fn exec(&self, regs: &mut CoreRegs, site: LibSite, _: &()) -> ExecStep { - match self { - ControlFlowOp::Fail => ExecStep::Fail, - ControlFlowOp::Test => { - if regs.st0 { - ExecStep::Next - } else { - ExecStep::Fail - } - } - ControlFlowOp::Jmp(offset) => { - regs.jmp().map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Fail) - } - ControlFlowOp::Jif(offset) => { - if regs.st0 { - regs.jmp().map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Fail) - } else { - ExecStep::Next - } - } - ControlFlowOp::Routine(offset) => { - regs.call(site).map(|_| ExecStep::Jump(*offset)).unwrap_or(ExecStep::Fail) - } - ControlFlowOp::Call(site) => { - regs.call(*site).map(|_| ExecStep::Call(*site)).unwrap_or(ExecStep::Fail) - } - ControlFlowOp::Exec(site) => { - regs.jmp().map(|_| ExecStep::Call(*site)).unwrap_or(ExecStep::Fail) - } - ControlFlowOp::Ret => regs.ret().map(ExecStep::Call).unwrap_or(ExecStep::Stop), - } - } -} - -impl InstructionSet for PutOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { BTreeSet::new() } - - fn dst_regs(&self) -> BTreeSet { - match self { - PutOp::ClrA(_, _) | PutOp::ClrF(_, _) | PutOp::ClrR(_, _) => BTreeSet::new(), - PutOp::PutA(reg, reg32, _) => bset![Reg::A(*reg, *reg32)], - PutOp::PutF(reg, reg32, _) => bset![Reg::F(*reg, *reg32)], - PutOp::PutR(reg, reg32, _) => bset![Reg::R(*reg, *reg32)], - PutOp::PutIfA(reg, reg32, _) => bset![Reg::A(*reg, *reg32)], - PutOp::PutIfR(reg, reg32, _) => bset![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_n(reg, index, MaybeNumber::none()); - } - PutOp::ClrF(reg, index) => { - regs.set_n(reg, index, MaybeNumber::none()); - } - PutOp::ClrR(reg, index) => { - regs.set_n(reg, index, MaybeNumber::none()); - } - PutOp::PutA(reg, index, number) => { - if !regs.set_n(reg, index, **number) { - regs.st0 = false; - } - } - PutOp::PutF(reg, index, number) => { - if !regs.set_n(reg, index, **number) { - regs.st0 = false; - } - } - PutOp::PutR(reg, index, number) => { - if !regs.set_n(reg, index, **number) { - regs.st0 = false; - } - } - PutOp::PutIfA(reg, index, number) => { - if !regs.set_n_if(reg, index, **number) { - regs.st0 = false; - } - } - PutOp::PutIfR(reg, index, number) => { - if !regs.set_n_if(reg, index, **number) { - regs.st0 = false; - } - } - }; - ExecStep::Next - } -} - -impl InstructionSet for MoveOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { - match self { - MoveOp::MovA(reg, idx1, _idx2) => { - bset![Reg::A(*reg, *idx1)] - } - MoveOp::DupA(reg, idx1, _idx2) => { - bset![Reg::A(*reg, *idx1)] - } - MoveOp::SwpA(reg, idx1, idx2) => { - bset![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] - } - MoveOp::MovF(reg, idx1, _idx2) => { - bset![Reg::F(*reg, *idx1)] - } - MoveOp::DupF(reg, idx1, _idx2) => { - bset![Reg::F(*reg, *idx1)] - } - MoveOp::SwpF(reg, idx1, idx2) => { - bset![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] - } - MoveOp::MovR(reg, idx1, _idx2) => { - bset![Reg::R(*reg, *idx1)] - } - MoveOp::DupR(reg, idx1, _idx2) => { - bset![Reg::R(*reg, *idx1)] - } - - MoveOp::CpyA(sreg, sidx, _dreg, _didx) => { - bset![Reg::A(*sreg, *sidx)] - } - MoveOp::CnvA(sreg, sidx, _dreg, _didx) => { - bset![Reg::A(*sreg, *sidx)] - } - MoveOp::CnvF(sreg, sidx, _dreg, _didx) => { - bset![Reg::F(*sreg, *sidx)] - } - MoveOp::CpyR(sreg, sidx, _dreg, _didx) => { - bset![Reg::R(*sreg, *sidx)] - } - MoveOp::SpyAR(sreg, sidx, dreg, didx) => { - bset![Reg::A(*sreg, *sidx), Reg::R(*dreg, *didx)] - } - MoveOp::CnvAF(sreg, sidx, _dreg, _didx) => { - bset![Reg::A(*sreg, *sidx)] - } - MoveOp::CnvFA(sreg, sidx, _dreg, _didx) => { - bset![Reg::F(*sreg, *sidx)] - } - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - MoveOp::MovA(reg, idx1, idx2) => { - bset![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] - } - MoveOp::DupA(reg, _idx1, idx2) => { - bset![Reg::A(*reg, *idx2)] - } - MoveOp::SwpA(reg, idx1, idx2) => { - bset![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] - } - MoveOp::MovF(reg, idx1, idx2) => { - bset![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] - } - MoveOp::DupF(reg, _idx1, idx2) => { - bset![Reg::F(*reg, *idx2)] - } - MoveOp::SwpF(reg, idx1, idx2) => { - bset![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] - } - MoveOp::MovR(reg, idx1, idx2) => { - bset![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] - } - MoveOp::DupR(reg, _idx1, idx2) => { - bset![Reg::R(*reg, *idx2)] - } - - MoveOp::CpyA(_sreg, _sidx, dreg, didx) => { - bset![Reg::A(*dreg, *didx)] - } - MoveOp::CnvA(_sreg, _sidx, dreg, didx) => { - bset![Reg::A(*dreg, *didx)] - } - MoveOp::CnvF(_sreg, _sidx, dreg, didx) => { - bset![Reg::F(*dreg, *didx)] - } - MoveOp::CpyR(_sreg, _sidx, dreg, didx) => { - bset![Reg::R(*dreg, *didx)] - } - MoveOp::SpyAR(sreg, sidx, dreg, didx) => { - bset![Reg::A(*sreg, *sidx), Reg::R(*dreg, *didx)] - } - MoveOp::CnvAF(_sreg, _sidx, dreg, didx) => { - bset![Reg::F(*dreg, *didx)] - } - MoveOp::CnvFA(_sreg, _sidx, dreg, didx) => { - bset![Reg::A(*dreg, *didx)] - } - } - } - - fn complexity(&self) -> u64 { 1 } - - fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { - match self { - MoveOp::MovA(reg, idx1, idx2) => { - regs.set_n(reg, idx2, regs.get_n(reg, idx1)); - regs.set_n(reg, idx1, MaybeNumber::none()); - } - MoveOp::DupA(reg, idx1, idx2) => { - regs.set_n(reg, idx2, regs.get_n(reg, idx1)); - } - MoveOp::SwpA(reg, idx1, idx2) => { - 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_n(reg, idx2, regs.get_n(reg, idx1)); - regs.set_n(reg, idx1, MaybeNumber::none()); - } - MoveOp::DupF(reg, idx1, idx2) => { - regs.set_n(reg, idx2, regs.get_n(reg, idx1)); - } - MoveOp::SwpF(reg, idx1, idx2) => { - 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_n(reg, idx2, regs.get_n(reg, idx1)); - regs.set_n(reg, idx1, MaybeNumber::none()); - } - MoveOp::DupR(reg, idx1, idx2) => { - regs.set_n(reg, idx2, regs.get_n(reg, idx1)); - } - - MoveOp::CpyA(sreg, sidx, dreg, didx) => { - let mut val = regs.get_n(sreg, sidx); - regs.st0 = val.reshape(dreg.layout()); - regs.set_n(dreg, didx, val); - } - MoveOp::CnvA(sreg, sidx, dreg, didx) => { - let mut val = regs.get_n(sreg, sidx); - regs.st0 = val.reshape(dreg.layout().into_signed()); - regs.set_n(dreg, didx, val); - } - MoveOp::CnvF(sreg, sidx, dreg, didx) => { - let mut val = regs.get_n(sreg, sidx); - regs.st0 = val.reshape(dreg.layout()); - regs.set_n(dreg, didx, val); - } - MoveOp::CpyR(sreg, sidx, dreg, didx) => { - let mut val = regs.get_n(sreg, sidx); - regs.st0 = val.reshape(dreg.layout()); - regs.set_n(dreg, didx, val); - } - MoveOp::SpyAR(sreg, sidx, 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_n(dreg, didx, val1); - regs.set_n(sreg, sidx, val2); - } - MoveOp::CnvAF(sreg, sidx, dreg, didx) => { - let mut val = regs.get_n(sreg, sidx); - regs.st0 = val.reshape(dreg.layout()); - regs.set_n(dreg, didx, val); - } - MoveOp::CnvFA(sreg, sidx, dreg, didx) => { - let mut val = regs.get_n(sreg, sidx); - regs.st0 = val.reshape(dreg.layout()); - regs.set_n(dreg, didx, val); - } - } - ExecStep::Next - } -} - -impl InstructionSet for CmpOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { - match self { - CmpOp::GtA(_, reg, idx1, idx2) => { - bset![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] - } - CmpOp::LtA(_, reg, idx1, idx2) => { - bset![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] - } - CmpOp::GtF(_, reg, idx1, idx2) => { - bset![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] - } - CmpOp::LtF(_, reg, idx1, idx2) => { - bset![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] - } - CmpOp::GtR(reg, idx1, idx2) => { - bset![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] - } - CmpOp::LtR(reg, idx1, idx2) => { - bset![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] - } - CmpOp::EqA(_, reg, idx1, idx2) => { - bset![Reg::A(*reg, *idx1), Reg::A(*reg, *idx2)] - } - CmpOp::EqF(_, reg, idx1, idx2) => { - bset![Reg::F(*reg, *idx1), Reg::F(*reg, *idx2)] - } - CmpOp::EqR(_, reg, idx1, idx2) => { - bset![Reg::R(*reg, *idx1), Reg::R(*reg, *idx2)] - } - - CmpOp::IfZA(reg, idx) | CmpOp::IfNA(reg, idx) => { - bset![Reg::A(*reg, *idx)] - } - CmpOp::IfZR(reg, idx) | CmpOp::IfNR(reg, idx) => { - bset![Reg::R(*reg, *idx)] - } - CmpOp::St(_, _, _) => BTreeSet::new(), - CmpOp::StInv => BTreeSet::new(), - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - CmpOp::St(_, reg, idx) => { - bset![Reg::A(*reg, (*idx).into())] - } - _ => BTreeSet::new(), - } - } - - fn complexity(&self) -> u64 { 1 } - - fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { - match self { - CmpOp::GtA(sign_flag, reg, idx1, idx2) => { - 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_n2(reg, idx1, reg, idx2).map(|(val1, val2)| { - if *eq_flag == FloatEqFlag::Rounding { - val1.rounding_cmp(&val2) - } else { - val1.cmp(&val2) - } - }) == Some(Ordering::Greater); - } - CmpOp::GtR(reg, idx1, idx2) => { - 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_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_n2(reg, idx1, reg, idx2).map(|(val1, val2)| { - if *eq_flag == FloatEqFlag::Rounding { - val1.rounding_cmp(&val2) - } else { - val1.cmp(&val2) - } - }) == Some(Ordering::Less); - } - CmpOp::LtR(reg, idx1, idx2) => { - 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_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_n2(reg, idx1, reg, idx2) - .map(|(val1, val2)| { - if *eq_flag == FloatEqFlag::Rounding { - val1.rounding_eq(&val2) - } else { - val1 == val2 - } - }) - .unwrap_or(false); - } - CmpOp::EqR(st, reg, idx1, idx2) => { - regs.st0 = regs - .get_n2(reg, idx1, reg, idx2) - .map(|(val1, val2)| val1 == val2) - .unwrap_or(*st == NoneEqFlag::Equal); - } - CmpOp::IfZA(reg, idx) => { - regs.st0 = regs.get_n(reg, idx).map(Number::is_zero).unwrap_or(false) - } - CmpOp::IfZR(reg, idx) => { - regs.st0 = regs.get_n(reg, idx).map(Number::is_zero).unwrap_or(false) - } - 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_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), - (Some(val), MergeFlag::And) => val & st, - (Some(val), MergeFlag::Or) => val | st, - }; - regs.set_n(reg, idx, Some(res)); - } - CmpOp::StInv => { - regs.st0 = !regs.st0; - } - } - ExecStep::Next - } -} - -impl InstructionSet for ArithmeticOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { - match self { - ArithmeticOp::Neg(reg, idx) | ArithmeticOp::Abs(reg, idx) => { - bset![Reg::new(*reg, *idx)] - } - ArithmeticOp::Stp(reg, idx, _) => { - bset![Reg::A(*reg, *idx)] - } - ArithmeticOp::AddA(_, reg, src, srcdst) - | ArithmeticOp::SubA(_, reg, src, srcdst) - | ArithmeticOp::MulA(_, reg, src, srcdst) - | ArithmeticOp::DivA(_, reg, src, srcdst) => { - bset![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) => { - bset![Reg::F(*reg, *src), Reg::F(*reg, *srcdst)] - } - ArithmeticOp::Rem(reg1, src, reg2, srcdst) => { - bset![Reg::A(*reg1, *src), Reg::A(*reg2, *srcdst)] - } - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - ArithmeticOp::Neg(reg, idx) | ArithmeticOp::Abs(reg, idx) => { - bset![Reg::new(*reg, *idx)] - } - ArithmeticOp::Stp(reg, idx, _) => { - bset![Reg::A(*reg, *idx)] - } - ArithmeticOp::AddA(_, reg, _src, srcdst) - | ArithmeticOp::SubA(_, reg, _src, srcdst) - | ArithmeticOp::MulA(_, reg, _src, srcdst) - | ArithmeticOp::DivA(_, reg, _src, srcdst) => { - bset![Reg::A(*reg, *srcdst)] - } - ArithmeticOp::AddF(_, reg, _src, srcdst) - | ArithmeticOp::SubF(_, reg, _src, srcdst) - | ArithmeticOp::MulF(_, reg, _src, srcdst) - | ArithmeticOp::DivF(_, reg, _src, srcdst) => { - bset![Reg::F(*reg, *srcdst)] - } - ArithmeticOp::Rem(_reg1, _src, reg2, srcdst) => { - bset![Reg::A(*reg2, *srcdst)] - } - } - } - - #[inline] - fn complexity(&self) -> u64 { - match self { - ArithmeticOp::AddF(_, _, _, _) - | ArithmeticOp::SubF(_, _, _, _) - | ArithmeticOp::MulF(_, _, _, _) - | ArithmeticOp::DivF(_, _, _, _) => 10, - - ArithmeticOp::AddA(_, _, _, _) - | ArithmeticOp::SubA(_, _, _, _) - | ArithmeticOp::MulA(_, _, _, _) - | ArithmeticOp::DivA(_, _, _, _) - | ArithmeticOp::Rem(_, _, _, _) - | ArithmeticOp::Stp(_, _, _) - | ArithmeticOp::Neg(_, _) - | ArithmeticOp::Abs(_, _) => 1, - } - } - - fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { - let is_some = match self { - ArithmeticOp::Abs(reg, idx) => { - regs.set_n(reg, idx, regs.get_n(reg, idx).and_then(Number::abs)) - } - ArithmeticOp::AddA(flags, reg, src, srcdst) => { - let res = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.int_add(val2, *flags)); - regs.set_n(reg, srcdst, res) - } - ArithmeticOp::AddF(flags, reg, src, srcdst) => { - let res: Option = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.float_add(val2, *flags).into()); - regs.set_n(reg, srcdst, res) - } - ArithmeticOp::SubA(flags, reg, src, srcdst) => { - let res = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.int_sub(val2, *flags)); - regs.set_n(reg, srcdst, res) - } - ArithmeticOp::SubF(flags, reg, src, srcdst) => { - let res: Option = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.float_sub(val2, *flags).into()); - regs.set_n(reg, srcdst, res) - } - ArithmeticOp::MulA(flags, reg, src, srcdst) => { - let res = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.int_mul(val2, *flags)); - regs.set_n(reg, srcdst, res) - } - ArithmeticOp::MulF(flags, reg, src, srcdst) => { - let res: Option = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.float_mul(val2, *flags).into()); - regs.set_n(reg, srcdst, res) - } - ArithmeticOp::DivA(flags, reg, src, srcdst) => { - let res = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.int_div(val2, *flags)); - regs.set_n(reg, srcdst, res) - } - ArithmeticOp::DivF(flags, reg, src, srcdst) => { - let res: Option = regs - .get_n2(reg, src, reg, srcdst) - .and_then(|(val1, val2)| val1.float_div(val2, *flags).into()); - regs.set_n(reg, srcdst, res) && !res.map(Number::is_nan).unwrap_or(false) - } - ArithmeticOp::Rem(reg1, idx1, reg2, idx2) => { - let 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_n( - reg, - idx, - regs.get_n(reg, idx).and_then(|val| { - if step.as_i8() < 0 { - let mut n = Number::from(-step.as_i8()); - debug_assert!( - n.reshape(val.layout()), - "reshape target byte length is always greater" - ); - val.int_sub(n, IntFlags { - signed: false, - wrap: false, - }) - } else { - let mut n = Number::from(*step); - debug_assert!( - n.reshape(val.layout()), - "reshape target byte length is always greater" - ); - val.int_add(n, IntFlags { - signed: false, - wrap: false, - }) - } - }), - ), - ArithmeticOp::Neg(reg, idx) => { - regs.set_n(reg, idx, regs.get_n(reg, idx).and_then(Number::neg)) - } - }; - regs.st0 = is_some; - ExecStep::Next - } -} - -impl InstructionSet for BitwiseOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { - match self { - BitwiseOp::And(reg, idx1, idx2, _idx3) - | BitwiseOp::Or(reg, idx1, idx2, _idx3) - | BitwiseOp::Xor(reg, idx1, idx2, _idx3) => { - bset![Reg::new(*reg, *idx1), Reg::new(*reg, *idx2)] - } - BitwiseOp::Not(reg, idx) => { - bset![Reg::new(*reg, *idx)] - } - - BitwiseOp::Shl(a2, shift, reg, idx) => { - bset![Reg::new(*a2, *shift), Reg::new(*reg, *idx)] - } - BitwiseOp::ShrA(_, a2, shift, reg, idx) => { - bset![Reg::new(*a2, *shift), Reg::A(*reg, *idx)] - } - BitwiseOp::ShrR(a2, shift, reg, idx) => { - bset![Reg::new(*a2, *shift), Reg::R(*reg, *idx)] - } - - BitwiseOp::Scl(a2, shift, reg, idx) => { - bset![Reg::new(*a2, *shift), Reg::new(*reg, *idx)] - } - BitwiseOp::Scr(a2, shift, reg, idx) => { - bset![Reg::new(*a2, *shift), Reg::new(*reg, *idx)] - } - - BitwiseOp::RevA(reg, idx) => { - bset![Reg::A(*reg, *idx)] - } - BitwiseOp::RevR(reg, idx) => { - bset![Reg::R(*reg, *idx)] - } - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - BitwiseOp::And(reg, _idx1, _idx2, idx3) - | BitwiseOp::Or(reg, _idx1, _idx2, idx3) - | BitwiseOp::Xor(reg, _idx1, _idx2, idx3) => { - bset![Reg::new(*reg, *idx3)] - } - BitwiseOp::Not(reg, idx) => { - bset![Reg::new(*reg, *idx)] - } - - BitwiseOp::Shl(_, _, reg, idx) => { - bset![Reg::new(*reg, *idx)] - } - BitwiseOp::ShrA(_, _, _, reg, idx) => { - bset![Reg::A(*reg, *idx)] - } - BitwiseOp::ShrR(_, _, reg, idx) => { - bset![Reg::R(*reg, *idx)] - } - - BitwiseOp::Scl(_, _, reg, idx) => { - bset![Reg::new(*reg, *idx)] - } - BitwiseOp::Scr(_, _, reg, idx) => { - bset![Reg::new(*reg, *idx)] - } - - BitwiseOp::RevA(reg, idx) => { - bset![Reg::A(*reg, *idx)] - } - BitwiseOp::RevR(reg, idx) => { - bset![Reg::R(*reg, *idx)] - } - } - } - - fn complexity(&self) -> u64 { 1 } - - 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]; - let word_shift = shift / 8; - let bit_shift = shift % 8; - for i in 0..n_bytes { - // Shift - if bit_shift < 8 && i + word_shift < n_bytes { - ret[i + word_shift] += original[i] << bit_shift; - } - // Carry - if bit_shift > 0 && i + word_shift + 1 < n_bytes { - ret[i + word_shift + 1] += original[i] >> (8 - bit_shift); - } - } - ret - } - fn shr(original: &[u8], shift: usize, n_bytes: usize) -> [u8; 1024] { - let mut ret = [0u8; 1024]; - let word_shift = shift / 8; - let bit_shift = shift % 8; - for i in word_shift..n_bytes { - // Shift - ret[i - word_shift] += original[i] >> bit_shift; - // Carry - if bit_shift > 0 && i < n_bytes - 1 { - ret[i - word_shift] += original[i + 1] << (8 - bit_shift); - } - } - ret - } - match self { - BitwiseOp::And(reg, src1, src2, dst) => { - regs.op(reg, src1, reg, src2, reg, dst, BitAnd::bitand) - } - BitwiseOp::Or(reg, src1, src2, dst) => { - regs.op(reg, src1, reg, src2, reg, dst, BitOr::bitor) - } - BitwiseOp::Xor(reg, src1, src2, dst) => { - regs.op(reg, src1, reg, src2, reg, dst, BitXor::bitxor) - } - BitwiseOp::Not(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_n(a, srcdst).unwrap_or_default()[a.bytes() - 1] & 0x80; - regs.st0 = msb == 0x80; - regs.op(reg2, srcdst, reg1, shift, reg2, srcdst, Shl::shl) - } - RegAR::R(r) => { - let shift = match reg1 { - RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, - RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, - }; - if let Some(original) = regs.get_r_mut(*r, srcdst) { - let msb = original.last().copied().unwrap_or_default() & 0x80; - let n_bytes = reg2.bytes() as usize; - original.copy_from_slice(&shl(original, shift, n_bytes)[..n_bytes]); - regs.st0 = msb == 0x80; - } - } - }, - BitwiseOp::ShrA(flag, reg1, shift, reg2, srcdst) => { - 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 { - val.into_signed().shr(shift) - } else { - val.shr(shift) - } - }); - regs.set_n(reg2, srcdst, res); - } - BitwiseOp::ShrR(reg1, shift, reg2, srcdst) => { - let shift = match reg1 { - RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, - RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, - }; - if let Some(original) = regs.get_r_mut(*reg2, srcdst) { - let lsb = original[0] & 1; - let n_bytes = reg2.bytes() as usize; - original.copy_from_slice(&shr(original, shift, n_bytes)[..n_bytes]); - regs.st0 = lsb == 1; - } - } - BitwiseOp::Scl(reg1, shift, reg2, srcdst) => match reg2 { - RegAR::A(_) => { - 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) - } - RegAR::R(r) => { - let shift = match reg1 { - RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, - RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, - }; - let shift = shift % reg2.bits() as usize; - if let Some(original) = regs.get_r_mut(*r, srcdst) { - let msb = original.last().copied().unwrap_or_default() & 0x80; - let n_bytes = reg2.bytes() as usize; - let mut shl = shl(original, shift, n_bytes); - let shr = shr(original, reg2.bits() as usize - shift, n_bytes); - for i in 0..n_bytes { - shl[i] |= shr[i]; - } - original.copy_from_slice(&shl[..n_bytes]); - regs.st0 = msb == 0x80; - } - } - }, - BitwiseOp::Scr(reg1, shift, reg2, srcdst) => match reg2 { - RegAR::A(_) => { - 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) - } - RegAR::R(r) => { - let shift = match reg1 { - RegA2::A8 => regs.a8[shift.to_usize()].unwrap_or_default() as usize, - RegA2::A16 => regs.a16[shift.to_usize()].unwrap_or_default() as usize, - }; - let shift = shift % reg2.bits() as usize; - if let Some(original) = regs.get_r_mut(*r, srcdst) { - let lsb = original[0] & 1; - let n_bytes = reg2.bytes() as usize; - let mut shr = shr(original, shift, n_bytes); - let shl = shl(original, reg2.bits() as usize - shift, n_bytes); - for i in 0..n_bytes { - shr[i] |= shl[i]; - } - original.copy_from_slice(&shr[..n_bytes]); - regs.st0 = lsb == 1; - } - } - }, - BitwiseOp::RevA(reg, idx) => { - 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) { - original.reverse(); - original.iter_mut().for_each(|byte| *byte = byte.reverse_bits()); - } - } - } - ExecStep::Next - } -} - -impl InstructionSet for BytesOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { - match self { - BytesOp::Put(_reg, _, _) => BTreeSet::new(), - BytesOp::Swp(reg1, reg2) | BytesOp::Find(reg1, reg2) => { - bset![Reg::S(*reg1), Reg::S(*reg2)] - } - BytesOp::Mov(reg1, _reg2) | BytesOp::Rev(reg1, _reg2) => { - bset![Reg::S(*reg1)] - } - BytesOp::Fill(reg, offset1, offset2, value, _) => { - bset![ - Reg::S(*reg), - Reg::A(RegA::A16, *offset1), - Reg::A(RegA::A16, *offset2), - Reg::A(RegA::A8, *value) - ] - } - BytesOp::Len(src, _reg, _dst) => { - bset![Reg::S(*src)] - } - BytesOp::Cnt(src, byte, _cnt) => { - bset![Reg::S(*src), Reg::new(RegA::A8, *byte)] - } - BytesOp::Eq(reg1, reg2) => { - bset![Reg::S(*reg1), Reg::S(*reg2)] - } - BytesOp::Con(reg1, reg2, no, _offset, _len) => { - bset![Reg::S(*reg1), Reg::S(*reg2), Reg::A(RegA::A16, *no),] - } - BytesOp::Extr(src, _dst, _index, offset) => { - bset![Reg::S(*src), Reg::new(RegA::A16, *offset)] - } - BytesOp::Inj(src1, src2, index, offset) => { - bset![Reg::S(*src1), Reg::new(*src2, *index), Reg::new(RegA::A16, *offset)] - } - BytesOp::Join(src1, src2, _dst) => { - bset![Reg::S(*src1), Reg::S(*src2)] - } - BytesOp::Splt(_flag, offset, src, _dst1, _dst2) => { - bset![Reg::A(RegA::A16, *offset), Reg::S(*src)] - } - BytesOp::Ins(_flag, offset, src, _dst) => { - bset![Reg::A(RegA::A16, *offset), Reg::S(*src)] - } - BytesOp::Del(_flag, reg1, offset1, reg2, offset2, _flag1, _flag2, src, _dst) => { - bset![Reg::new(*reg1, *offset1), Reg::new(*reg2, *offset2), Reg::S(*src)] - } - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - BytesOp::Put(reg, _, _) => { - bset![Reg::S(*reg)] - } - BytesOp::Swp(reg1, reg2) | BytesOp::Find(reg1, reg2) => { - bset![Reg::S(*reg1), Reg::S(*reg2)] - } - BytesOp::Mov(_reg1, reg2) | BytesOp::Rev(_reg1, reg2) => { - bset![Reg::S(*reg2)] - } - BytesOp::Fill(reg, _offset1, _offset2, _value, _) => { - bset![Reg::S(*reg)] - } - BytesOp::Len(_src, reg, dst) => { - bset![Reg::A(*reg, *dst)] - } - BytesOp::Cnt(_src, _byte, cnt) => { - bset![Reg::new(RegA::A16, *cnt)] - } - BytesOp::Eq(_reg1, _reg2) => BTreeSet::new(), - BytesOp::Con(_reg1, _reg2, _no, offset, len) => { - bset![Reg::A(RegA::A16, *offset), Reg::A(RegA::A16, *len)] - } - BytesOp::Extr(_src, dst, index, _offset) => { - bset![Reg::new(*dst, *index)] - } - BytesOp::Inj(src1, _src2, _index, _offset) => { - bset![Reg::S(*src1)] - } - BytesOp::Join(_src1, _src2, dst) => { - bset![Reg::S(*dst)] - } - BytesOp::Splt(_flag, _offset, _src, dst1, dst2) => { - bset![Reg::S(*dst1), Reg::S(*dst2)] - } - BytesOp::Ins(_flag, _offset, _src, dst) => { - bset![Reg::S(*dst)] - } - BytesOp::Del(_flag, _reg1, _offset1, _reg2, _offset2, _flag1, _flag2, _src, dst) => { - bset![Reg::S(*dst)] - } - } - } - - #[inline] - fn complexity(&self) -> u64 { 5 } - - #[allow(warnings)] - fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { - match self { - BytesOp::Put(reg, bytes, st0) => { - regs.s16[reg.as_usize()] = Some(*bytes.clone()); - if *st0 { - regs.st0 = false - } - } - BytesOp::Mov(reg1, reg2) => { - let bs = regs.s16[reg1.as_usize()].clone(); - regs.s16[reg1.as_usize()] = None; - regs.s16[reg2.as_usize()] = bs; - } - BytesOp::Swp(reg1, reg2) => { - let bs1 = regs.s16[reg1.as_usize()].clone(); - let bs2 = regs.s16[reg2.as_usize()].clone(); - regs.s16[reg1.as_usize()] = bs2; - regs.s16[reg2.as_usize()] = bs1; - } - BytesOp::Fill(reg, offset1, offset2, value, flag) => { - let mut f = || -> Option<()> { - let o1 = regs.a16[offset1.to_usize()]?; - let o2 = regs.a16[offset2.to_usize()]?; - let range = o1..o2; - let val = regs.a8[value.to_usize()]?; - let ref mut bs = regs.s16[reg.as_usize()]; - let bs = if let Some(s) = bs { - s - } else { - *bs = Some(ByteStr::default()); - bs.as_mut().expect("rust optionals are broken") - }; - if bs.len() <= range.end && *flag == ExtendFlag::Fail { - return None; - } - bs.fill(range, val); - Some(()) - }; - f().unwrap_or_else(|| regs.st0 = false); - } - BytesOp::Len(src, reg, dst) => { - let mut f = || -> Option<()> { - let s = regs.get_s(*src)?; - let len = s.len(); - if !reg.int_layout().fits_usize(len as usize) { - return None; - } - regs.set_n(reg, dst, len as u32); - Some(()) - }; - f().unwrap_or_else(|| { - regs.st0 = false; - regs.set_n(reg, dst, MaybeNumber::none()); - }); - } - BytesOp::Cnt(src, byte, dst) => { - let mut f = || -> Option<()> { - let val = regs.a8[*byte as u8 as usize]?; - let bs = regs.s16[src.as_usize()].as_ref()?; - let count = bs.as_ref().into_iter().filter(|b| **b == val).count(); - if !RegA::A16.int_layout().fits_usize(count) { - return None; - } - regs.set_n(RegA::A16, dst, count as u32); - Some(()) - }; - f().unwrap_or_else(|| { - regs.st0 = false; - regs.set_n(RegA::A16, dst, MaybeNumber::none()); - }); - } - BytesOp::Eq(reg1, reg2) => { - let s1 = regs.get_s(*reg1); - let s2 = regs.get_s(*reg2); - regs.st0 = match (s1, s2) { - (Some(s1), Some(s2)) => s1 == s2, - (None, None) => true, - _ => false, - }; - } - BytesOp::Find(reg1, reg2) => { - let mut f = || -> Option<()> { - 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_n(RegA::A16, Reg32::Reg0, count as u16); - Some(()) - }; - f().unwrap_or_else(|| { - regs.st0 = false; - regs.set_n(RegA::A16, Reg32::Reg0, MaybeNumber::none()); - }) - } - BytesOp::Rev(reg1, reg2) => { - let mut f = || -> Option<()> { - let mut s = regs.get_s(*reg1)?.clone(); - let bs = s.as_mut(); - bs.reverse(); - regs.s16[reg2.as_usize()] = Some(s); - Some(()) - }; - f().unwrap_or_else(|| { - regs.st0 = false; - regs.s16[reg2.as_usize()] = None; - }) - } - BytesOp::Con(reg1, reg2, n, offset_dst, len_dst) => { - let mut f = || -> Option<()> { - let (s1, s2) = (regs.get_s(*reg1)?, regs.get_s(*reg2)?); - let (r1, r2) = (s1.as_ref(), s2.as_ref()); - let n = regs.a16[*n as u8 as usize]?; - let size = ::core::cmp::min(s1.len(), s2.len()); - let mut elems = (0..) - .zip(r1.iter().zip(r2).map(|(c1, c2)| c1 == c2)) - .take(size as usize) - .skip_while(|(_, c)| !*c); - for _ in 0..n { - while let Some((_, false)) = elems.next() {} - while let Some((_, true)) = elems.next() {} - } - let begin = elems.next(); - let end = elems.skip_while(|(_, c)| *c).next(); - let (offset, len) = match (begin, end) { - (Some((b, _)), Some((e, _))) => (b, e - b), - (Some((b, _)), None) => (b, size - b), - _ => return None, - }; - 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_n(RegA::A16, offset_dst, MaybeNumber::none()); - regs.set_n(RegA::A16, len_dst, MaybeNumber::none()); - }) - } - BytesOp::Extr(src, dst, index, offset) => { - let mut f = || -> Option<()> { - let s_len = regs.get_s(*src)?.len(); - let offset = regs.a16[*offset as u8 as usize].filter(|e| *e < s_len)?; - let end = offset - .checked_add(dst.layout().bytes()) - .filter(|e| *e <= s_len) - .unwrap_or_else(|| { - regs.st0 = false; - s_len - }); - let num = Number::from_slice( - ®s.get_s(*src)?.as_ref()[offset as usize..end as usize], - ); - regs.set_n(dst, index, num); - Some(()) - }; - f().unwrap_or_else(|| { - regs.st0 = false; - 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_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); - s.as_mut()[offset as usize..=end as usize].copy_from_slice(val.as_ref()); - regs.s16[src.as_usize()] = Some(s); - Some(()) - }; - f().unwrap_or_else(|| { - regs.st0 = false; - regs.set_n(dst, index, MaybeNumber::none()); - }) - } - BytesOp::Join(src1, src2, dst) => { - let mut f = || -> Option<()> { - let (s1, s2) = regs.get_s2(*src1, *src2)?; - if s1.len() as usize + s2.len() as usize > u16::MAX as usize { - return None; - } - let len = s1.len() + s2.len(); - let mut d = s1.clone(); - d.adjust_len(len); - let mut d = ByteStr::with(s1); - d.as_mut()[s1.len() as usize..].copy_from_slice(s2.as_ref()); - regs.s16[dst.as_usize()] = Some(d); - Some(()) - }; - f().unwrap_or_else(|| { - regs.st0 = false; - regs.s16[dst.as_usize()] = None; - }) - } - BytesOp::Splt(flag, offset, src, dst1, dst2) => { - todo!("#(6) complete bytestring opcode implementation") - } - BytesOp::Ins(flag, offset, src, dst) => { - todo!("#(6) complete bytestring opcode implementation") - } - BytesOp::Del(flag, reg1, offset1, reg2, offset2, flag1, flag2, src, dst) => { - todo!("#(6) complete bytestring opcode implementation") - } - } - ExecStep::Next - } -} - -impl InstructionSet for DigestOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::with(constants::ISA_ID_BPDIGEST) } - - fn src_regs(&self) -> BTreeSet { - match self { - DigestOp::Ripemd(src, _dst) - | DigestOp::Sha256(src, _dst) - | DigestOp::Blake3(src, _dst) - | DigestOp::Sha512(src, _dst) => bset![Reg::S(*src)], - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - DigestOp::Ripemd(_src, dst) => bset![Reg::new(RegR::R160, *dst)], - DigestOp::Sha256(_src, dst) => bset![Reg::new(RegR::R256, *dst)], - DigestOp::Blake3(_src, dst) => bset![Reg::new(RegR::R256, *dst)], - DigestOp::Sha512(_src, dst) => bset![Reg::new(RegR::R512, *dst)], - } - } - - #[inline] - fn complexity(&self) -> u64 { 100 } - - fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { - let none; - match self { - DigestOp::Ripemd(src, dst) => { - let s = regs.s16(*src); - none = s.is_none(); - let hash = s.map(|s| { - let mut hash: [u8; 20] = ripemd::Ripemd160::digest(s.as_ref()).into(); - // RIPEMD-160 is big-endian - hash.reverse(); - hash - }); - regs.set_n(RegR::R160, dst, hash); - } - DigestOp::Sha256(src, dst) => { - let s = regs.s16(*src); - none = s.is_none(); - let hash: Option<[u8; 32]> = s.map(|s| sha2::Sha256::digest(s.as_ref()).into()); - regs.set_n(RegR::R256, dst, hash); - } - DigestOp::Blake3(src, dst) => { - let s = regs.s16(*src); - none = s.is_none(); - let hash: Option<[u8; 32]> = s.map(|s| blake3::hash(s.as_ref()).into()); - regs.set_n(RegR::R256, dst, hash); - } - DigestOp::Sha512(src, dst) => { - let s = regs.s16(*src); - none = s.is_none(); - let hash: Option<[u8; 64]> = s.map(|s| sha2::Sha512::digest(s.as_ref()).into()); - regs.set_n(RegR::R512, dst, hash); - } - } - if none { - regs.st0 = false; - } - ExecStep::Next - } -} - -impl InstructionSet for Secp256k1Op { - type Context<'ctx> = (); - - #[cfg(not(feature = "secp256k1"))] - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - #[cfg(feature = "secp256k1")] - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::with(constants::ISA_ID_SECP256K) } - - fn src_regs(&self) -> BTreeSet { - match self { - Secp256k1Op::Gen(src, _dst) => { - bset![Reg::R(RegR::R256, *src)] - } - Secp256k1Op::Mul(RegBlockAR::A, scal, src, _dst) => { - bset![Reg::A(RegA::A256, *scal), Reg::R(RegR::R512, *src)] - } - Secp256k1Op::Mul(RegBlockAR::R, scal, src, _dst) => { - bset![Reg::R(RegR::R256, *scal), Reg::R(RegR::R512, *src)] - } - Secp256k1Op::Add(src, srcdst) => { - bset![Reg::R(RegR::R512, *src), Reg::new(RegR::R512, *srcdst)] - } - Secp256k1Op::Neg(src, _dst) => { - bset![Reg::R(RegR::R512, *src)] - } - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - Secp256k1Op::Gen(_src, dst) => { - bset![Reg::new(RegR::R512, *dst)] - } - Secp256k1Op::Mul(_, _, _src, dst) => { - bset![Reg::R(RegR::R512, *dst)] - } - Secp256k1Op::Add(_src, srcdst) => { - bset![Reg::new(RegR::R512, *srcdst)] - } - Secp256k1Op::Neg(_src, dst) => { - bset![Reg::new(RegR::R512, *dst)] - } - } - } - - #[inline] - fn complexity(&self) -> u64 { 1000 } - - #[cfg(not(feature = "secp256k1"))] - fn exec(&self, _: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { - unimplemented!("AluVM runtime compiled without support for Secp256k1 instructions") - } - - #[cfg(feature = "secp256k1")] - fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { - use secp256k1::{PublicKey, SecretKey, SECP256K1}; - - match self { - Secp256k1Op::Gen(src, dst) => { - let res = regs - .get_n(RegR::R256, src) - .and_then(|mut src| { - let src = src.as_mut(); - // little endian to big endian - src.reverse(); - SecretKey::from_slice(src).ok() - }) - .map(|sk| PublicKey::from_secret_key(SECP256K1, &sk)) - .as_ref() - .map(PublicKey::serialize_uncompressed) - .map(|pk| Number::from_slice(&pk[1..])); - 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_n(reg, scal) - .and_then(|scal| { - regs.get_n(RegR::R512, src) - .and_then(|val| { - let mut pk = [4u8; 65]; - pk[1..].copy_from_slice(val.as_ref()); - PublicKey::from_slice(&pk).ok() - }) - .map(|pk| (scal, pk)) - }) - .and_then(|(scal, pk)| { - let mut buf = [0u8; 32]; - buf.copy_from_slice(scal.as_ref()); - let scal = secp256k1::Scalar::from_le_bytes(buf).ok()?; - pk.mul_tweak(SECP256K1, &scal).ok() - }) - .as_ref() - .map(PublicKey::serialize_uncompressed) - .map(|pk| Number::from_slice(&pk[1..])); - regs.set_n(RegR::R512, dst, res); - } - - Secp256k1Op::Add(src, srcdst) => { - let res = regs - .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_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)) - }) - }) - .and_then(|(pk1, pk2)| pk1.combine(&pk2).ok()) - .as_ref() - .map(PublicKey::serialize_uncompressed) - .map(|pk| Number::from_slice(&pk[1..])); - regs.set_n(RegR::R512, srcdst, res); - } - - Secp256k1Op::Neg(src, dst) => { - let res = regs - .get_n(RegR::R512, src) - .and_then(|val| { - let mut pk = [4u8; 65]; - pk[1..].copy_from_slice(&val[..]); - PublicKey::from_slice(&pk).ok() - }) - .map(|pk| pk.negate(SECP256K1)) - .as_ref() - .map(PublicKey::serialize_uncompressed) - .map(|pk| Number::from_slice(&pk[1..])); - regs.set_n(RegR::R512, dst, res); - } - } - ExecStep::Next - } -} - -impl InstructionSet for Curve25519Op { - type Context<'ctx> = (); - - #[cfg(not(feature = "curve25519"))] - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - #[cfg(feature = "curve25519")] - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::with(constants::ISA_ID_ED25519) } - - fn src_regs(&self) -> BTreeSet { - match self { - Curve25519Op::Gen(src, _dst) => { - bset![Reg::R(RegR::R256, *src)] - } - Curve25519Op::Mul(RegBlockAR::A, scal, src, _dst) => { - bset![Reg::A(RegA::A256, *scal), Reg::R(RegR::R512, *src)] - } - Curve25519Op::Mul(RegBlockAR::R, scal, src, _dst) => { - bset![Reg::R(RegR::R256, *scal), Reg::R(RegR::R512, *src)] - } - Curve25519Op::Add(src1, src2, _dst, _) => { - bset![Reg::R(RegR::R512, *src1), Reg::new(RegR::R512, *src2)] - } - Curve25519Op::Neg(src, _dst) => { - bset![Reg::R(RegR::R512, *src)] - } - } - } - - fn dst_regs(&self) -> BTreeSet { - match self { - Curve25519Op::Gen(_src, dst) => { - bset![Reg::new(RegR::R512, *dst)] - } - Curve25519Op::Mul(_, _, _src, dst) => { - bset![Reg::R(RegR::R512, *dst)] - } - Curve25519Op::Add(_src1, _src2, dst, _) => { - bset![Reg::new(RegR::R512, *dst)] - } - Curve25519Op::Neg(_src, dst) => { - bset![Reg::new(RegR::R512, *dst)] - } - } - } - - #[inline] - fn complexity(&self) -> u64 { 1000 } - - #[cfg(not(feature = "curve25519"))] - fn exec(&self, _: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { - unimplemented!("AluVM runtime compiled without support for Curve25519 instructions") - } - - #[cfg(feature = "curve25519")] - fn exec(&self, _regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { - todo!("implement Curve256 operations") - } -} - -impl InstructionSet for ReservedOp { - type Context<'ctx> = (); - - #[inline] - fn isa_ids() -> IsaSeg { IsaSeg::default() } - - fn src_regs(&self) -> BTreeSet { BTreeSet::new() } - - fn dst_regs(&self) -> BTreeSet { BTreeSet::new() } - - fn complexity(&self) -> u64 { u64::MAX } - - fn exec(&self, regs: &mut CoreRegs, site: LibSite, ctx: &()) -> ExecStep { - ControlFlowOp::Fail.exec(regs, site, ctx) - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[cfg(feature = "secp256k1")] - use crate::reg::{Reg8, RegBlockAR}; - - #[test] - fn bytes_con_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - let s1 = "apple_banana_kiwi".as_bytes(); - let s2 = "apple@banana@kiwi".as_bytes(); - BytesOp::Put(1.into(), Box::new(ByteStr::with(s1)), false).exec( - &mut register, - lib_site, - &(), - ); - BytesOp::Put(2.into(), Box::new(ByteStr::with(s2)), false).exec( - &mut register, - lib_site, - &(), - ); - // apple (0th fragment) - PutOp::PutA(RegA::A16, Reg32::Reg0, MaybeNumber::from(0).into()).exec( - &mut register, - lib_site, - &(), - ); - BytesOp::Con(1.into(), 2.into(), Reg32::Reg0, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - 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( - &mut register, - lib_site, - &(), - ); - BytesOp::Con(1.into(), 2.into(), Reg32::Reg0, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - 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( - &mut register, - lib_site, - &(), - ); - BytesOp::Con(1.into(), 2.into(), Reg32::Reg0, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - 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( - &mut register, - lib_site, - &(), - ); - BytesOp::Con(1.into(), 2.into(), Reg32::Reg0, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - 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(); - let s2 = "bbb".as_bytes(); - BytesOp::Put(1.into(), Box::new(ByteStr::with(s1)), false).exec( - &mut register, - lib_site, - &(), - ); - BytesOp::Put(2.into(), Box::new(ByteStr::with(s2)), false).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutA(RegA::A16, Reg32::Reg0, MaybeNumber::from(0).into()).exec( - &mut register, - lib_site, - &(), - ); - BytesOp::Con(1.into(), 2.into(), Reg32::Reg0, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - 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); - CmpOp::StInv.exec(&mut register, lib_site, &()); - assert!(register.st0); - ControlFlowOp::Test.exec(&mut register, lib_site, &()); - - let s1 = [0u8; u16::MAX as usize]; - let s2 = [0u8; u16::MAX as usize]; - BytesOp::Put(1.into(), Box::new(ByteStr::with(s1)), false).exec( - &mut register, - lib_site, - &(), - ); - BytesOp::Put(2.into(), Box::new(ByteStr::with(s2)), false).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutA(RegA::A16, Reg32::Reg0, MaybeNumber::from(0).into()).exec( - &mut register, - lib_site, - &(), - ); - BytesOp::Con(1.into(), 2.into(), Reg32::Reg0, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - 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, - lib_site, - &(), - ); - BytesOp::Con(1.into(), 2.into(), Reg32::Reg0, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - 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); - } - - #[test] - #[cfg(feature = "secp256k1")] - fn secp256k1_add_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - PutOp::PutR(RegR::R256, Reg32::Reg0, MaybeNumber::from(600u16).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg1, MaybeNumber::from(1200u16).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg2, MaybeNumber::from(1800u16).into()).exec( - &mut register, - lib_site, - &(), - ); - Secp256k1Op::Gen(Reg32::Reg0, Reg8::Reg0).exec(&mut register, lib_site, &()); - Secp256k1Op::Gen(Reg32::Reg1, Reg8::Reg1).exec(&mut register, lib_site, &()); - Secp256k1Op::Add(Reg32::Reg0, Reg8::Reg1).exec(&mut register, lib_site, &()); - Secp256k1Op::Gen(Reg32::Reg2, Reg8::Reg2).exec(&mut register, lib_site, &()); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - } - - #[test] - #[cfg(feature = "secp256k1")] - fn secp256k1_mul_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - PutOp::PutR(RegR::R256, Reg32::Reg0, MaybeNumber::from(2u8).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg1, MaybeNumber::from(3u8).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg2, MaybeNumber::from(6u8).into()).exec( - &mut register, - lib_site, - &(), - ); - Secp256k1Op::Gen(Reg32::Reg0, Reg8::Reg0).exec(&mut register, lib_site, &()); - Secp256k1Op::Mul(RegBlockAR::R, Reg32::Reg1, Reg32::Reg0, Reg32::Reg1).exec( - &mut register, - lib_site, - &(), - ); - Secp256k1Op::Gen(Reg32::Reg2, Reg8::Reg2).exec(&mut register, lib_site, &()); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - } - - #[test] - #[cfg(feature = "secp256k1")] - fn secp256k1_neg_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - PutOp::PutR(RegR::R256, Reg32::Reg0, MaybeNumber::from(1u8).into()).exec( - &mut register, - lib_site, - &(), - ); - Secp256k1Op::Gen(Reg32::Reg0, Reg8::Reg0).exec(&mut register, lib_site, &()); - Secp256k1Op::Neg(Reg32::Reg0, Reg8::Reg1).exec(&mut register, lib_site, &()); - Secp256k1Op::Neg(Reg32::Reg1, Reg8::Reg2).exec(&mut register, lib_site, &()); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg0, Reg32::Reg1).exec( - &mut register, - lib_site, - &(), - ); - assert!(!register.st0); - ControlFlowOp::Test.exec(&mut register, lib_site, &()); - assert!(!register.st0); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg0, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - PutOp::PutR(RegR::R256, Reg32::Reg4, MaybeNumber::from(5u8).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg5, MaybeNumber::from(6u8).into()).exec( - &mut register, - lib_site, - &(), - ); - Secp256k1Op::Gen(Reg32::Reg4, Reg8::Reg4).exec(&mut register, lib_site, &()); - Secp256k1Op::Gen(Reg32::Reg5, Reg8::Reg5).exec(&mut register, lib_site, &()); - // -G + 6G - Secp256k1Op::Add(Reg32::Reg1, Reg8::Reg5).exec(&mut register, lib_site, &()); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R512, Reg32::Reg4, Reg32::Reg5).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - } - - /* TODO: Enable after curve25519 re-implementation - #[test] - #[cfg(feature = "curve25519")] - fn curve25519_mul_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - PutOp::PutR(RegR::R256, Reg32::Reg0, MaybeNumber::from(2u8).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg1, MaybeNumber::from(3u8).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg2, MaybeNumber::from(6u8).into()).exec( - &mut register, - lib_site, - &(), - ); - Curve25519Op::Gen(Reg32::Reg0, Reg8::Reg0).exec(&mut register, lib_site, &()); - Curve25519Op::Mul(RegBlockAR::R, Reg32::Reg1, Reg32::Reg0, Reg32::Reg1).exec( - &mut register, - lib_site, - &(), - ); - Curve25519Op::Gen(Reg32::Reg2, Reg8::Reg2).exec(&mut register, lib_site, &()); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg1, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg0, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - assert!(!register.st0); - } - - #[test] - #[cfg(feature = "curve25519")] - fn curve25519_add_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - PutOp::PutR(RegR::R256, Reg32::Reg0, MaybeNumber::from(600u16).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg1, MaybeNumber::from(1200u16).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg2, MaybeNumber::from(1800u16).into()).exec( - &mut register, - lib_site, - &(), - ); - Curve25519Op::Gen(Reg32::Reg0, Reg8::Reg0).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( - &mut register, - lib_site, - &(), - ); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg2, Reg32::Reg3).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - } - - #[test] - #[cfg(feature = "curve25519")] - fn curve25519_add_overflow_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - let l_plus_two_bytes: [u8; 32] = [ - 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, - 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, - ]; - PutOp::PutR( - RegR::R256, - Reg32::Reg0, - MaybeNumber::from(Number::from_slice(l_plus_two_bytes)).into(), - ) - .exec(&mut register, lib_site, &()); - PutOp::PutR(RegR::R256, Reg32::Reg1, MaybeNumber::from(1u8).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg2, MaybeNumber::from(3u8).into()).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::Reg7, Reg32::Reg1, Reg32::Reg3, false).exec( - &mut register, - lib_site, - &(), - ); - 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!(register.st0); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg2, Reg32::Reg3).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - } - - #[test] - #[cfg(feature = "curve25519")] - fn curve25519_neg_test() { - let mut register = CoreRegs::default(); - let lib_site = LibSite::default(); - PutOp::PutR(RegR::R256, Reg32::Reg0, MaybeNumber::from(1u8).into()).exec( - &mut register, - lib_site, - &(), - ); - 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::R256, Reg32::Reg0, Reg32::Reg1).exec( - &mut register, - lib_site, - &(), - ); - assert!(!register.st0); - ControlFlowOp::Succ.exec(&mut register, lib_site, &()); - assert!(register.st0); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg0, Reg32::Reg2).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - PutOp::PutR(RegR::R256, Reg32::Reg4, MaybeNumber::from(5u8).into()).exec( - &mut register, - lib_site, - &(), - ); - PutOp::PutR(RegR::R256, Reg32::Reg5, MaybeNumber::from(6u8).into()).exec( - &mut register, - lib_site, - &(), - ); - Curve25519Op::Gen(Reg32::Reg4, Reg8::Reg4).exec(&mut register, lib_site, &()); - Curve25519Op::Gen(Reg32::Reg5, Reg8::Reg5).exec(&mut register, lib_site, &()); - // -G + 6G - Curve25519Op::Add(Reg32::Reg1, Reg32::Reg5, Reg32::Reg6, true).exec( - &mut register, - lib_site, - &(), - ); - CmpOp::EqR(NoneEqFlag::NonEqual, RegR::R256, Reg32::Reg4, Reg32::Reg6).exec( - &mut register, - lib_site, - &(), - ); - assert!(register.st0); - } - */ -} diff --git a/src/isa/flags.rs b/src/isa/flags.rs deleted file mode 100644 index 7b334b3..0000000 --- a/src/isa/flags.rs +++ /dev/null @@ -1,1022 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -//! Flags used by operation codes - -#[cfg(feature = "alloc")] -use alloc::borrow::ToOwned; -#[cfg(feature = "alloc")] -use alloc::string::String; -use core::fmt::{self, Display, Formatter, Write}; -use core::str::FromStr; - -use amplify::num::apfloat::Round; -use amplify::num::{u1, u2, u3}; - -/// Marker trait for flag types -pub trait Flag: FromStr + Default {} - -/// Errors for parsing string representation for a flag values -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[cfg_attr(feature = "std", derive(Error))] -#[display(doc_comments)] -pub enum ParseFlagError { - /// unknown `{0}` flag `{1}` - UnknownFlag(/** Flag description */ &'static str, /** Unrecognized flag */ char), - - /// unknown `{0}` flags `{1}` - UnknownFlags(/** Flag description */ &'static str, /** Unrecognized flags */ String), - - /// only one of mutually exclusive flags must be specified for {0} (only `{1}` or `{2}`) - MutuallyExclusiveFlags( - /** Flag description */ &'static str, - /** Flag 1 */ char, - /** Flag 2 */ char, - ), - - /// required flag for {0} is absent - RequiredFlagAbsent(/** Flag description */ &'static str), - - /// duplicated flags `{1}` are specified for {0} - DuplicatedFlags(/** Flag description */ &'static str, /** List of duplicated flags */ String), -} - -/// Integer encoding flag -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum SignFlag { - /// Unsigned integer - #[display("u")] - Unsigned = 0, - - /// Signed integer - #[display("s")] - Signed = 1, -} - -impl Flag for SignFlag {} - -impl Default for SignFlag { - #[inline] - fn default() -> Self { Self::Unsigned } -} - -impl FromStr for SignFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("integer sign")); - } - let filtered = s.replace(&['u', 's'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("integer sign", filtered)); - } - match (s.contains('u'), s.contains('s')) { - (true, false) => Ok(SignFlag::Unsigned), - (false, true) => Ok(SignFlag::Signed), - (true, true) => Err(ParseFlagError::MutuallyExclusiveFlags("integer sign", 'u', 's')), - (false, false) => Err(ParseFlagError::RequiredFlagAbsent("integer sign")), - } - } -} - -impl SignFlag { - /// Constructs integer sign flag from `u1` value (used in bytecode serialization) - pub fn from_u1(val: u1) -> SignFlag { - match val.into_u8() { - v if v == SignFlag::Unsigned as u8 => SignFlag::Unsigned, - v if v == SignFlag::Signed as u8 => SignFlag::Signed, - _ => unreachable!(), - } - } - - /// Returns `u1` representation of integer sign flag (used in bytecode serialization). - pub fn as_u1(self) -> u1 { u1::with(self as u8) } -} - -impl From for SignFlag { - fn from(val: u1) -> SignFlag { SignFlag::from_u1(val) } -} - -impl From<&SignFlag> for u1 { - fn from(flag: &SignFlag) -> u1 { flag.as_u1() } -} - -impl From for u1 { - fn from(flag: SignFlag) -> u1 { flag.as_u1() } -} - -impl From for bool { - fn from(flag: SignFlag) -> Self { flag == SignFlag::Signed } -} - -impl From<&SignFlag> for bool { - fn from(flag: &SignFlag) -> Self { *flag == SignFlag::Signed } -} - -/// Non-equality flag -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum NoneEqFlag { - /// Two `None` register values are considered equal - #[display("e")] - Equal = 1, - - /// Two `None` register values are considered non-equal - #[display("n")] - NonEqual = 0, -} - -impl Flag for NoneEqFlag {} - -impl Default for NoneEqFlag { - #[inline] - fn default() -> Self { Self::Equal } -} - -impl FromStr for NoneEqFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("none-equality")); - } - let filtered = s.replace(&['e', 'n'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("none-equality", filtered)); - } - match (s.contains('e'), s.contains('n')) { - (true, false) => Ok(NoneEqFlag::Equal), - (false, true) => Ok(NoneEqFlag::NonEqual), - (true, true) => Err(ParseFlagError::MutuallyExclusiveFlags("none-equality", 'e', 'n')), - (false, false) => Err(ParseFlagError::RequiredFlagAbsent("none-equality")), - } - } -} - -impl NoneEqFlag { - /// Constructs none-equality flag from `u1` value (used in bytecode serialization) - pub fn from_u1(val: u1) -> NoneEqFlag { - match val.into_u8() { - v if v == NoneEqFlag::Equal as u8 => NoneEqFlag::Equal, - v if v == NoneEqFlag::NonEqual as u8 => NoneEqFlag::NonEqual, - _ => unreachable!(), - } - } - - /// Returns `u1` representation of none-equality flag (used in bytecode serialization). - pub fn as_u1(self) -> u1 { u1::with(self as u8) } -} - -impl From for NoneEqFlag { - fn from(val: u1) -> NoneEqFlag { NoneEqFlag::from_u1(val) } -} - -impl From<&NoneEqFlag> for u1 { - fn from(flag: &NoneEqFlag) -> u1 { flag.as_u1() } -} - -impl From for u1 { - fn from(flag: NoneEqFlag) -> u1 { flag.as_u1() } -} - -impl From for bool { - fn from(flag: NoneEqFlag) -> Self { flag == NoneEqFlag::Equal } -} - -impl From<&NoneEqFlag> for bool { - fn from(flag: &NoneEqFlag) -> Self { *flag == NoneEqFlag::Equal } -} - -/// Float equality flag -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum FloatEqFlag { - /// Use exact match, when nearest floats are always non-equal. - /// - /// NB: This still implies `+0` == `-0`. - #[display("e")] - Exact = 0, - - /// Use rounded matching, when floats which differ only on a single bit in significand are - /// still treated as euqal. - #[display("r")] - Rounding = 1, -} - -impl Flag for FloatEqFlag {} - -impl Default for FloatEqFlag { - #[inline] - fn default() -> Self { Self::Exact } -} - -impl FromStr for FloatEqFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("float equality")); - } - let filtered = s.replace(&['e', 'r'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("float equality", filtered)); - } - match (s.contains('e'), s.contains('r')) { - (true, false) => Ok(FloatEqFlag::Exact), - (false, true) => Ok(FloatEqFlag::Rounding), - (true, true) => Err(ParseFlagError::MutuallyExclusiveFlags("float equality", 'e', 'r')), - (false, false) => Err(ParseFlagError::RequiredFlagAbsent("float equality")), - } - } -} - -impl FloatEqFlag { - /// Constructs float equality flag from `u1` value (used in bytecode serialization) - pub fn from_u1(val: u1) -> FloatEqFlag { - match val.into_u8() { - v if v == FloatEqFlag::Exact as u8 => FloatEqFlag::Exact, - v if v == FloatEqFlag::Rounding as u8 => FloatEqFlag::Rounding, - _ => unreachable!(), - } - } - - /// Returns `u1` representation of float equality flag (used in bytecode serialization). - pub fn as_u1(self) -> u1 { u1::with(self as u8) } -} - -impl From for FloatEqFlag { - fn from(val: u1) -> FloatEqFlag { FloatEqFlag::from_u1(val) } -} - -impl From<&FloatEqFlag> for u1 { - fn from(flag: &FloatEqFlag) -> u1 { flag.as_u1() } -} - -impl From for u1 { - fn from(flag: FloatEqFlag) -> u1 { flag.as_u1() } -} - -/// Rounding flags for float numbers -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum RoundingFlag { - /// Round always toward zero, which means ceiling for negative numbers and flooring for - /// positive numbers. - #[display("z")] - TowardsZero = 0, - - /// Round to the nearest neighbour, and if the number is exactly in the middle, ties round to - /// the nearest even digit in the required position. - #[display("n")] - TowardsNearest = 1, - - /// Round down (flooring), ie toward -∞; negative results thus round away from zero. - #[display("f")] - Floor = 2, - - /// Round up (ceiling), ie toward +∞; negative results thus round toward zero. - #[display("c")] - Ceil = 3, -} - -impl Flag for RoundingFlag {} - -impl Default for RoundingFlag { - #[inline] - fn default() -> Self { Self::TowardsNearest } -} - -impl FromStr for RoundingFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("float rounding")); - } - - let filtered = s.replace(&['n', 'z', 'c', 'f'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("float rounding", filtered)); - } - if s.len() > 1 { - return Err(ParseFlagError::MutuallyExclusiveFlags( - "float rounding", - s.as_bytes()[0].into(), - s.as_bytes()[1].into(), - )); - } - - if s.contains('n') { - Ok(RoundingFlag::TowardsNearest) - } else if s.contains('z') { - Ok(RoundingFlag::TowardsZero) - } else if s.contains('c') { - Ok(RoundingFlag::Ceil) - } else if s.contains('f') { - Ok(RoundingFlag::Floor) - } else { - Err(ParseFlagError::UnknownFlag("float rounding", s.as_bytes()[0].into())) - } - } -} - -impl RoundingFlag { - /// Constructs float rounding flag from `u2` value (used in bytecode serialization) - pub fn from_u2(val: u2) -> Self { - match val.to_u8() { - v if v == RoundingFlag::TowardsZero as u8 => RoundingFlag::TowardsZero, - v if v == RoundingFlag::TowardsNearest as u8 => RoundingFlag::TowardsNearest, - v if v == RoundingFlag::Ceil as u8 => RoundingFlag::Ceil, - v if v == RoundingFlag::Floor as u8 => RoundingFlag::Floor, - _ => unreachable!(), - } - } - - /// Returns `u2` representation of float rounding flag (used in bytecode serialization). - pub fn as_u2(self) -> u2 { u2::with(self as u8) } -} - -impl From for RoundingFlag { - fn from(val: u2) -> RoundingFlag { RoundingFlag::from_u2(val) } -} - -impl From<&RoundingFlag> for u2 { - fn from(flag: &RoundingFlag) -> u2 { flag.as_u2() } -} - -impl From for u2 { - fn from(flag: RoundingFlag) -> u2 { flag.as_u2() } -} - -impl From for Round { - fn from(flag: RoundingFlag) -> Self { - match flag { - RoundingFlag::TowardsZero => Round::TowardZero, - RoundingFlag::TowardsNearest => Round::NearestTiesToEven, - RoundingFlag::Floor => Round::TowardNegative, - RoundingFlag::Ceil => Round::TowardPositive, - } - } -} - -/// Encoding and overflowing flags for integer numbers -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] -pub struct IntFlags { - /// Treat the integer as signed (`true`) or unsigned (`false`). Signed integers has a different - /// behaviour on detecting overflows, since they use only 7 bits for significant digits and not - /// 8. - pub signed: bool, - - /// With addition / subtraction / multiplication, indicates whether overflow must result in - /// modulo-based wrapping (`true`) or set the destination into `None` state (`false`). - /// With division, `true` means that Euclidean division should be performed. - pub wrap: bool, -} - -impl Flag for IntFlags {} - -impl Display for IntFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if self.signed { - f.write_char('s')?; - } else { - f.write_char('u')?; - } - if self.wrap { - f.write_char('w') - } else { - f.write_char('c') - } - } -} - -impl FromStr for IntFlags { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - let signed = match (s.contains('s'), s.contains('u')) { - (true, false) => true, - (false, true) => false, - (true, true) => { - return Err(ParseFlagError::MutuallyExclusiveFlags( - "integer serialization", - 's', - 'u', - )); - } - (false, false) => { - return Err(ParseFlagError::RequiredFlagAbsent("integer serialization")); - } - }; - let wrap = match (s.contains('w'), s.contains('c')) { - (true, false) => true, - (false, true) => false, - (true, true) => { - return Err(ParseFlagError::MutuallyExclusiveFlags("overflow", 'w', 'c')); - } - (false, false) => return Err(ParseFlagError::RequiredFlagAbsent("overflow")), - }; - if s.len() > 2 { - return Err(ParseFlagError::UnknownFlags( - "integer serialization", - s.replace(&['s', 'u', 'c', 'w'][..], ""), - )); - } - - Ok(IntFlags { signed, wrap }) - } -} - -impl IntFlags { - /// Constructs integer arithmetic flags from `u2` value (used in bytecode serialization) - pub fn from_u2(val: u2) -> Self { - let val = val.to_u8(); - IntFlags { - signed: val & 0x01 == 1, - wrap: val & 0x02 >> 1 == 1, - } - } - - /// Returns `u2` representation of integer arithmetic flags (used in bytecode serialization). - pub fn as_u2(self) -> u2 { u2::with(self.signed as u8 | ((self.wrap as u8) << 1)) } - - /// Constructs variant for unsigned checked operation flags - #[inline] - pub fn unsigned_checked() -> Self { - IntFlags { - signed: false, - wrap: false, - } - } - - /// Constructs variant for signed checked operation flags - #[inline] - pub fn signed_checked() -> Self { - IntFlags { - signed: true, - wrap: false, - } - } - - /// Constructs variant for unsigned wrapped operation flags - #[inline] - pub fn unsigned_wrapped() -> Self { - IntFlags { - signed: false, - wrap: true, - } - } - - /// Constructs variant for signed wrapped operation flags - #[inline] - pub fn signed_wrapped() -> Self { - IntFlags { - signed: true, - wrap: true, - } - } -} - -impl From for IntFlags { - fn from(val: u2) -> IntFlags { IntFlags::from_u2(val) } -} - -impl From<&IntFlags> for u2 { - fn from(flag: &IntFlags) -> u2 { flag.as_u2() } -} - -impl From for u2 { - fn from(flag: IntFlags) -> u2 { flag.as_u2() } -} - -/// Merge flags for operations which need to add certain bit value to the register existing value -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum MergeFlag { - /// Assign the bit value to the register clearing its previous content - #[display("s")] - Set = 0, - - /// Add the bit value to the register value, treating existing register value as an unsigned - /// value. If the addition leads to an overflow, set `st0` register to `false` and keep the - /// register value at the maximum ("saturating" addition). Otherwise, do not modify `st0` - /// value. - #[display("a")] - Add = 1, - - /// Bit-and the bit and the lowest bit value from the register. - #[display("n")] - And = 2, - - /// Bit-or the bit and the lowest bit value from the register. - #[display("o")] - Or = 3, -} - -impl Flag for MergeFlag {} - -impl Default for MergeFlag { - #[inline] - fn default() -> Self { Self::Set } -} - -impl FromStr for MergeFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("merge operation")); - } - - let filtered = s.replace(&['s', 'a', 'n', 'o'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("merge operation", filtered)); - } - if s.len() > 1 { - return Err(ParseFlagError::MutuallyExclusiveFlags( - "merge", - s.as_bytes()[0].into(), - s.as_bytes()[1].into(), - )); - } - - if s.contains('s') { - Ok(MergeFlag::Set) - } else if s.contains('a') { - Ok(MergeFlag::Add) - } else if s.contains('n') { - Ok(MergeFlag::And) - } else if s.contains('o') { - Ok(MergeFlag::Or) - } else { - Err(ParseFlagError::UnknownFlag("merge operation", s.as_bytes()[0].into())) - } - } -} - -impl MergeFlag { - /// Constructs merge operation flag from `u2` value (used in bytecode serialization) - pub fn from_u2(val: u2) -> Self { - match val.to_u8() { - v if v == MergeFlag::Set as u8 => MergeFlag::Set, - v if v == MergeFlag::Add as u8 => MergeFlag::Add, - v if v == MergeFlag::And as u8 => MergeFlag::And, - v if v == MergeFlag::Or as u8 => MergeFlag::Or, - _ => unreachable!(), - } - } - - /// Returns `u2` representation of merge operation flag (used in bytecode serialization). - pub fn as_u2(self) -> u2 { u2::with(self as u8) } -} - -impl From for MergeFlag { - fn from(val: u2) -> MergeFlag { MergeFlag::from_u2(val) } -} - -impl From<&MergeFlag> for u2 { - fn from(flag: &MergeFlag) -> u2 { flag.as_u2() } -} - -impl From for u2 { - fn from(flag: MergeFlag) -> u2 { flag.as_u2() } -} - -/// Flag for bytestring operations indicating whether the string should be extended to a new length -/// or the operation should fail (for instance, see `fill` operation). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum ExtendFlag { - /// Unsigned integer - #[display("e")] - Extend = 0, - - /// Signed integer - #[display("f")] - Fail = 1, -} - -impl Flag for ExtendFlag {} - -impl Default for ExtendFlag { - #[inline] - fn default() -> Self { Self::Extend } -} - -impl FromStr for ExtendFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("extension flag")); - } - let filtered = s.replace(&['e', 'f'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("extension flag", filtered)); - } - match (s.contains('e'), s.contains('f')) { - (true, false) => Ok(ExtendFlag::Extend), - (false, true) => Ok(ExtendFlag::Fail), - (true, true) => Err(ParseFlagError::MutuallyExclusiveFlags("extension flag", 'e', 'f')), - (false, false) => Err(ParseFlagError::RequiredFlagAbsent("extension flag")), - } - } -} - -impl ExtendFlag { - /// Constructs extension flag from `u1` value (used in bytecode serialization) - pub fn from_u1(val: u1) -> ExtendFlag { - match val.into_u8() { - v if v == ExtendFlag::Extend as u8 => ExtendFlag::Extend, - v if v == ExtendFlag::Fail as u8 => ExtendFlag::Fail, - _ => unreachable!(), - } - } - - /// Returns `u1` representation of extension flag (used in bytecode serialization). - pub fn as_u1(self) -> u1 { u1::with(self as u8) } -} - -impl From for ExtendFlag { - fn from(val: u1) -> ExtendFlag { ExtendFlag::from_u1(val) } -} - -impl From<&ExtendFlag> for u1 { - fn from(flag: &ExtendFlag) -> u1 { flag.as_u1() } -} - -impl From for u1 { - fn from(flag: ExtendFlag) -> u1 { flag.as_u1() } -} - -impl From for bool { - fn from(flag: ExtendFlag) -> Self { flag == ExtendFlag::Fail } -} - -impl From<&ExtendFlag> for bool { - fn from(flag: &ExtendFlag) -> Self { *flag == ExtendFlag::Fail } -} - -/// Flags for bytestring split operation. -/// -/// If offset exceeds the length of the string in the register, than the behaviour of -/// [`crate::isa::BytesOp::Splt`] op code is defined by this flag. Please check its description -/// for more details. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum SplitFlag { - /// If the offset is equal to zero, exceeds or equal to the length of the source string sets - /// first and second destination register to `None`; `st0` to `false`. - /// - /// Matches case (1) in [`crate::isa::BytesOp::Splt`] description - #[display("n")] - NoneNone = 0, - - /// If the offset is equal to zero, sets first destination register to `None`, second is set to - /// `None` only if the string in the source register is empty; `st0` in both cases is set - /// to `false`. - /// - /// Matches case (2) in [`crate::isa::BytesOp::Splt`] description - #[display("nn")] - NoneNoneOnEmpty = 1, - - /// If the offset is equal to zero, sets first destination register to `None`, second is set to - /// an empty string if the string in the source register is empty; `st0` in both cases is - /// set to `false`. - /// - /// Matches case (3) in [`crate::isa::BytesOp::Splt`] description - #[display("nz")] - NoneZeroOnEmpty = 2, - - /// If the offset is equal to zero, sets first destination register to empty string, second is - /// set to an empty string if the string in the source register is empty; `st0` value - /// remain unchanged. - /// - /// Matches case (4) in [`crate::isa::BytesOp::Splt`] description - #[display("ee")] - ZeroZeroOnEmpty = 3, - - /// If the offset exceeds the length of the source string sets the first destination register - /// to the source string (<=offset in len) and second to `None`; `st0` value is set to - /// `false`. - /// - /// Matches case (5) in [`crate::isa::BytesOp::Splt`] description - #[display("cn")] - CutNone = 4, - - /// If the offset exceeds the length of the source string sets the first destination register - /// to the source string (<=offset in len) and second to zero-length string; `st0` value is - /// set to `false`. - /// - /// Matches case (6) in [`crate::isa::BytesOp::Splt`] description - #[display("cz")] - CutZero = 5, - - /// If the offset exceeds the length of the source string sets the first destination register - /// to zero-length string and second to `None`; `st0` value is set to `false`. - /// - /// Matches case (7) in [`crate::isa::BytesOp::Splt`] description - #[display("zn")] - ZeroNone = 6, - - /// If the offset exceeds the length of the source string sets both the first and second - /// destination registers to zero-length string; `st0` value is set to `false`. - /// - /// Matches case (8) in [`crate::isa::BytesOp::Splt`] description - #[display("zz")] - ZeroZero = 7, -} - -impl Flag for SplitFlag {} - -impl Default for SplitFlag { - #[inline] - fn default() -> Self { Self::NoneNone } -} - -impl FromStr for SplitFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("split operation")); - } - - Ok(match s { - "n" => SplitFlag::NoneNone, - "nn" => SplitFlag::NoneNoneOnEmpty, - "nz" => SplitFlag::NoneZeroOnEmpty, - "ee" => SplitFlag::ZeroZeroOnEmpty, - "cn" => SplitFlag::CutNone, - "cz" => SplitFlag::CutZero, - "zn" => SplitFlag::ZeroNone, - "zz" => SplitFlag::ZeroZero, - _ => return Err(ParseFlagError::UnknownFlags("split operation", s.to_owned())), - }) - } -} - -impl SplitFlag { - /// Constructs split operation flag from `u3` value (used in bytecode serialization) - pub fn from_u3(val: u3) -> Self { - match val.to_u8() { - v if v == SplitFlag::NoneNone as u8 => SplitFlag::NoneNone, - v if v == SplitFlag::NoneNoneOnEmpty as u8 => SplitFlag::NoneNoneOnEmpty, - v if v == SplitFlag::NoneZeroOnEmpty as u8 => SplitFlag::NoneZeroOnEmpty, - v if v == SplitFlag::ZeroZeroOnEmpty as u8 => SplitFlag::ZeroZeroOnEmpty, - v if v == SplitFlag::CutNone as u8 => SplitFlag::CutNone, - v if v == SplitFlag::CutZero as u8 => SplitFlag::CutZero, - v if v == SplitFlag::ZeroNone as u8 => SplitFlag::ZeroNone, - v if v == SplitFlag::ZeroZero as u8 => SplitFlag::ZeroZero, - _ => unreachable!(), - } - } - - /// Returns `u3` representation of split operation flag (used in bytecode serialization). - pub fn as_u3(self) -> u3 { u3::with(self as u8) } -} - -impl From for SplitFlag { - fn from(val: u3) -> Self { Self::from_u3(val) } -} - -impl From<&SplitFlag> for u3 { - fn from(flag: &SplitFlag) -> u3 { flag.as_u3() } -} - -impl From for u3 { - fn from(flag: SplitFlag) -> u3 { flag.as_u3() } -} - -/// Flags for bytestring insert operation. For the detailed description please read -/// [`crate::isa::BytesOp::Ins`]. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum InsertFlag { - /// Set destination to `None` if `offset < dst_len && src_len + dst_len > 2^16`. - /// - /// Matches case (6) in [`crate::isa::BytesOp::Ins`] description - #[display("l")] - FailOnLen = 0, - - /// Set destination to `None` if `offset > dst_len && src_len + dst_len + offset <= 2^16`. - /// - /// Matches case (1) in [`crate::isa::BytesOp::Ins`] description - #[display("o")] - FailOnOffset = 1, - - /// Set destination to `None` if `offset > dst_len && src_len + dst_len + offset > 2^16`. - /// - /// Matches case (4) in [`crate::isa::BytesOp::Ins`] description - #[display("f")] - FailOnOffsetLen = 2, - - /// Fill destination from `dst_let` to `offset` with zeros if - /// `offset > dst_len && src_len + dst_len + offset <= 2^16`. - /// - /// Matches case (2) in [`crate::isa::BytesOp::Ins`] description - #[display("e")] - Extend = 3, - - /// Use `src_len` instead of `offset` if - /// `offset > dst_len && src_len + dst_len + offset <= 2^16`. - /// - /// Matches case (3) in [`crate::isa::BytesOp::Ins`] description - #[display("a")] - Append = 4, - - /// Fill destination from `dst_let` to `offset` with zeros and cut source string part exceeding - /// `2^16` if `offset > dst_len && src_len + dst_len + offset > 2^16` - /// - /// Matches case (5) in [`crate::isa::BytesOp::Ins`] description - #[display("x")] - ExtendCut = 5, - - /// Cut destination string part exceeding `2^16` - /// - /// Matches case (7) in [`crate::isa::BytesOp::Ins`] description - #[display("c")] - Cut = 6, - - /// Reduce `src_len` such that it will fit the destination - /// - /// Matches case (8) in [`crate::isa::BytesOp::Ins`] description - #[display("s")] - Shorten = 7, -} - -impl Flag for InsertFlag {} - -impl Default for InsertFlag { - #[inline] - fn default() -> Self { Self::FailOnLen } -} - -impl FromStr for InsertFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("insert operation")); - } - let filtered = s.replace(&['l', 'o', 'f', 'e', 'a', 'x', 'c', 's'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("insert operation", filtered)); - } - if filtered.len() > 1 { - return Err(ParseFlagError::DuplicatedFlags("insert operation", filtered)); - } - - Ok(match filtered.as_bytes()[0].into() { - 'l' => InsertFlag::FailOnLen, - 'o' => InsertFlag::FailOnOffset, - 'f' => InsertFlag::FailOnOffsetLen, - 'e' => InsertFlag::Extend, - 'a' => InsertFlag::Append, - 'x' => InsertFlag::ExtendCut, - 'c' => InsertFlag::Cut, - 's' => InsertFlag::Shorten, - _ => unreachable!(), - }) - } -} - -impl InsertFlag { - /// Constructs insert operation flag from `u3` value (used in bytecode serialization) - pub fn from_u3(val: u3) -> Self { - match val.to_u8() { - v if v == InsertFlag::FailOnLen as u8 => InsertFlag::FailOnLen, - v if v == InsertFlag::FailOnOffset as u8 => InsertFlag::FailOnOffset, - v if v == InsertFlag::FailOnOffsetLen as u8 => InsertFlag::FailOnOffsetLen, - v if v == InsertFlag::Extend as u8 => InsertFlag::Extend, - v if v == InsertFlag::Append as u8 => InsertFlag::Append, - v if v == InsertFlag::ExtendCut as u8 => InsertFlag::ExtendCut, - v if v == InsertFlag::Cut as u8 => InsertFlag::Cut, - v if v == InsertFlag::Shorten as u8 => InsertFlag::Shorten, - _ => unreachable!(), - } - } - - /// Returns `u3` representation of insert operation flag (used in bytecode serialization). - pub fn as_u3(self) -> u3 { u3::with(self as u8) } -} - -impl From for InsertFlag { - fn from(val: u3) -> Self { Self::from_u3(val) } -} - -impl From<&InsertFlag> for u3 { - fn from(flag: &InsertFlag) -> u3 { flag.as_u3() } -} - -impl From for u3 { - fn from(flag: InsertFlag) -> u3 { flag.as_u3() } -} - -/// Flags for bytestring delete operation. For the detailed description please read -/// [`crate::isa::BytesOp::Del`]. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum DeleteFlag { - /// Set destination to `None` on any failure. - /// - /// Matches case (1) in [`crate::isa::BytesOp::Del`] description - #[display("n")] - None = 0, - - /// Set destination to zero-length string if `offset_start > src_len`. - /// - /// Matches case (2) in [`crate::isa::BytesOp::Del`] description - #[display("z")] - Zero = 1, - - /// Set destination to the fragment of the string `offset_start..src_len` if - /// `offset_end > src_len && offset_start <= src_len`. - /// - /// Matches case (3) in [`crate::isa::BytesOp::Del`] description - #[display("c")] - Cut = 2, - - /// Set destination to the fragment of the string `offset_start..src_len` and extend its length - /// up to `offset_end - offset_start` with trailing zeros if - /// `offset_end > src_len && offset_start <= src_len`. - /// - /// Matches case (4) in [`crate::isa::BytesOp::Del`] description - #[display("e")] - Extend = 3, -} - -impl Flag for DeleteFlag {} - -impl Default for DeleteFlag { - #[inline] - fn default() -> Self { Self::None } -} - -impl FromStr for DeleteFlag { - type Err = ParseFlagError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParseFlagError::RequiredFlagAbsent("delete operation")); - } - let filtered = s.replace(&['n', 'z', 'c', 'e'][..], ""); - if !filtered.is_empty() { - return Err(ParseFlagError::UnknownFlags("delete operation", filtered)); - } - if filtered.len() > 1 { - return Err(ParseFlagError::DuplicatedFlags("delete operation", filtered)); - } - - Ok(match filtered.as_bytes()[0].into() { - 'n' => DeleteFlag::None, - 'z' => DeleteFlag::Zero, - 'c' => DeleteFlag::Cut, - 'e' => DeleteFlag::Extend, - _ => unreachable!(), - }) - } -} - -impl DeleteFlag { - /// Constructs delete operation flag from `u2` value (used in bytecode serialization) - pub fn from_u2(val: u2) -> Self { - match val.to_u8() { - v if v == DeleteFlag::None as u8 => DeleteFlag::None, - v if v == DeleteFlag::Zero as u8 => DeleteFlag::Zero, - v if v == DeleteFlag::Cut as u8 => DeleteFlag::Cut, - v if v == DeleteFlag::Extend as u8 => DeleteFlag::Extend, - _ => unreachable!(), - } - } - - /// Returns `u2` representation of delete operation flag (used in bytecode serialization). - pub fn as_u2(self) -> u2 { u2::with(self as u8) } -} - -impl From for DeleteFlag { - fn from(val: u2) -> Self { Self::from_u2(val) } -} - -impl From<&DeleteFlag> for u2 { - fn from(flag: &DeleteFlag) -> u2 { flag.as_u2() } -} - -impl From for u2 { - fn from(flag: DeleteFlag) -> u2 { flag.as_u2() } -} diff --git a/src/isa/instr.rs b/src/isa/instr.rs index 19b91bd..d828dfa 100644 --- a/src/isa/instr.rs +++ b/src/isa/instr.rs @@ -3,961 +3,135 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. -#[cfg(feature = "alloc")] -use alloc::boxed::Box; +use alloc::collections::BTreeSet; +use core::fmt::{Debug, Display}; -use super::{ - DeleteFlag, FloatEqFlag, InsertFlag, InstructionSet, IntFlags, MergeFlag, RoundingFlag, - SignFlag, SplitFlag, -}; -use crate::data::{ByteStr, MaybeNumber, Step}; -use crate::isa::{ExtendFlag, NoneEqFlag}; -use crate::library::LibSite; -use crate::reg::{Reg16, Reg32, Reg8, RegA, RegA2, RegAF, RegAR, RegBlockAR, RegF, RegR, RegS}; +use amplify::confinement::TinyOrdSet; -/// Reserved instruction, which equal to [`ControlFlowOp::Fail`]. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Default)] -#[display("rsrv:{0:02X}")] -pub struct ReservedOp(/** Reserved instruction op code value */ pub(super) u8); +use crate::core::{Core, Register, Site, SiteId}; +use crate::isa::Bytecode; +use crate::{CoreExt, IsaId}; -/// Full set of instructions -#[derive(Clone, PartialEq, Eq, Hash, Debug, Display)] -#[display(inner)] -#[non_exhaustive] -pub enum Instr -where Extension: InstructionSet -{ - /// Control-flow instructions. See [`ControlFlowOp`] for the details. - // 0b00_000_*** - ControlFlow(ControlFlowOp), +/// Turing machine movement after instruction execution +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum ExecStep { + /// Stop program execution. + Stop, - /// Instructions setting register values. See [`PutOp`] for the details. - // 0b00_001_*** - Put(PutOp), + /// Set `CK` to `Fail` and halt the program execution. + FailHalt, - /// Instructions moving and swapping register values. See [`PutOp`] for the details. - // 0b00_010_*** - Move(MoveOp), + /// Move to the next instruction. + Next, - /// Instructions comparing register values. See [`CmpOp`] for the details. - // 0b00_011_*** - Cmp(CmpOp), + /// Move to the next instruction and set `CK` to `Fail`. + FailContinue, - /// Arithmetic instructions. See [`ArithmeticOp`] for the details. - // 0b00_100_*** - Arithmetic(ArithmeticOp), + /// Jump to the offset from the origin. + Jump(u16), - /// Bit operations & boolean algebra instructions. See [`BitwiseOp`] for the details. - // 0b00_101_*** - Bitwise(BitwiseOp), - - /// Operations on byte strings. See [`BytesOp`] for the details. - // 0b00_110_*** - Bytes(BytesOp), - - /// Cryptographic hashing functions. See [`DigestOp`] for the details. - // 0b01_000_*** - Digest(DigestOp), - - #[cfg(feature = "secp256k1")] - /// Operations on Secp256k1 elliptic curve. See [`Secp256k1Op`] for the details. - // 0b01_001_0** - Secp256k1(Secp256k1Op), - - #[cfg(feature = "curve25519")] - /// Operations on Curve25519 elliptic curve. See [`Curve25519Op`] for the details. - // 0b01_001_1** - Curve25519(Curve25519Op), - - /// Extension operations which can be provided by a host environment provided via generic - /// parameter - // 0b10_***_*** - ExtensionCodes(Extension), - - /// Reserved instruction for future use in core `ALU` ISA. - /// - /// Currently equal to [`ControlFlowOp::Fail`]. - ReservedInstruction(ReservedOp), - - // Reserved operations for the future use. - // - // When such an opcode is met in the bytecode the decoder MUST fail. - // 0x11_***_*** - /// No-operation instruction. - // #[value = 0b11_111_111] - Nop, -} - -/// Control-flow instructions -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum ControlFlowOp { - /// Completes program execution writing `false` to `st0` (indicating program failure). Does not - /// modify value of call stack registers. - #[display("fail")] - Fail, - - /// Checks the value of `st0` register. If the value is `false`, stops execution of the - /// program. Otherwise, it is a no-operation. - #[display("test")] - Test, - - /// Unconditionally jumps to an offset. Increments `cy0`. - #[display("jmp {0:#06X}")] - Jmp(u16), - - /// Jumps to an offset if `st0` == true, otherwise does nothing. Increments `cy0`. - #[display("jif {0:#06X}")] - Jif(u16), - - /// Jumps to other location in the current code with ability to return back (calls a - /// subroutine). Increments `cy0` and pushes offset of the instruction which follows current - /// one to `cs0`. - #[display("routine {0:#06X}")] - Routine(u16), - - /// Calls code from an external library identified by the hash of its code. Increments `cy0` - /// and `cp0` and pushes offset of the instruction which follows current one to `cs0`. - #[display("call {0}")] - Call(LibSite), - - /// Passes execution to other library without an option to return. Does not increment `cy0` and - /// `cp0` counters and does not add anything to the call stack `cs0`. - #[display("exec {0}")] - Exec(LibSite), - - /// Returns execution flow to the previous location from the top of `cs0`. Does not change the - /// value in `cy0`. Decrements `cp0`. - #[display("ret")] - Ret, + /// Jump to another code fragment. + Call(Site), } -/// Instructions setting register values -#[derive(Clone, PartialEq, Eq, Hash, Debug, Display)] -pub enum PutOp { - /// Cleans a value of `A` register (sets it to undefined state) - #[display("clr {0}{1}")] - ClrA(RegA, Reg32), - - /// Cleans a value of `F` register (sets it to undefined state) - #[display("clr {0}{1}")] - ClrF(RegF, Reg32), - - /// Cleans a value of `R` register (sets it to undefined state) - #[display("clr {0}{1}")] - ClrR(RegR, Reg32), - - /// Unconditionally assigns a value to `A` register. - /// - /// NB: Bytecode does not contain the value (it is contained in the data segment), thus when - /// this instruction is assembled and the data are not present in the data segment (their - /// offset + length exceeds data segment size) the operation will set destination register - /// into undefined state and `st0` to `false`. Otherwise, `st0` value is not affected. - #[display("put {0}{1},{2}")] - PutA(RegA, Reg32, Box), - - /// Unconditionally assigns a value to `F` register - /// - /// NB: Bytecode does not contain the value (it is contained in the data segment), thus when - /// this instruction is assembled and the data are not present in the data segment (their - /// offset + length exceeds data segment size) the operation will set destination register - /// into undefined state and `st0` to `false`. Otherwise, `st0` value is not affected. - #[display("put {0}{1},{2}")] - PutF(RegF, Reg32, Box), - - /// Unconditionally assigns a value to `R` register - /// - /// NB: Bytecode does not contain the value (it is contained in the data segment), thus when - /// this instruction is assembled and the data are not present in the data segment (their - /// offset + length exceeds data segment size) the operation will set destination register - /// into undefined state and `st0` to `false`. Otherwise, `st0` value is not affected. - #[display("put {0}{1},{2}")] - PutR(RegR, Reg32, Box), - - /// Conditionally assigns a value to `A` register if the register is in uninitialized state. - /// If the register is initialized and the value is not `None` sets `st0` to `false`. - /// - /// NB: Bytecode does not contain the value (it is contained in the data segment), thus when - /// this instruction is assembled and the data are not present in the data segment (their - /// offset + length exceeds data segment size) _and_ the destination register is - /// initialized, the operation will set destination register into undefined state and `st0` - /// to `false`. Otherwise, `st0` value is changed according to the general operation rules. - #[display("putif {0}{1},{2}")] - PutIfA(RegA, Reg32, Box), - - /// Conditionally assigns a value to `R` register if the register is in uninitialized state. - /// If the register is initialized and the value is not `None` sets `st0` to `false`. - /// - /// NB: Bytecode does not contain the value (it is contained in the data segment), thus when - /// this instruction is assembled and the data are not present in the data segment (their - /// offset + length exceeds data segment size) _and_ the destination register is - /// initialized, the operation will set destination register into undefined state and `st0` - /// to `false`. Otherwise, `st0` value is changed according to the general operation rules. - #[display("putif {0}{1},{2}")] - PutIfR(RegR, Reg32, Box), -} - -/// Instructions moving and swapping register values -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum MoveOp { - /// Move operation: moves value of one of the integer arithmetic registers into another integer - /// arithmetic register of the same bit size, clearing its previous value and setting the - /// source to `None`. - #[display("mov {0}{1},{0}{2}")] - MovA(RegA, Reg32, Reg32), - - /// Duplicate operation: duplicates value of one of the integer arithmetic registers into - /// another integer arithmetic register of the same bit size, clearing its previous value. - #[display("dup {0}{1},{0}{2}")] - DupA(RegA, Reg32, Reg32), - - /// Swap operation: swaps value of two integer arithmetic registers of the same bit size. - #[display("swp {0}{1},{0}{2}")] - SwpA(RegA, Reg32, Reg32), - - /// Move operation: moves value of one of the float arithmetic registers into another float - /// arithmetic register of the same bit size, clearing its previous value and setting the - /// source to `None`. - #[display("mov {0}{1},{0}{2}")] - MovF(RegF, Reg32, Reg32), - - /// Duplicate operation: duplicates value of one of the float arithmetic registers into - /// another float arithmetic register of the same bit size, clearing its previous value. - #[display("dup {0}{1},{0}{2}")] - DupF(RegF, Reg32, Reg32), - - /// Swap operation: swaps value of two float arithmetic registers of the same bit size. - #[display("swp {0}{1},{0}{2}")] - SwpF(RegF, Reg32, Reg32), - - /// Move operation: moves value of one of the general non-arithmetic registers into another - /// general non-arithmetic register of the same bit size, clearing its previous value and - /// setting the source to `None`. - #[display("mov {0}{1},{0}{2}")] - MovR(RegR, Reg32, Reg32), - - /// Duplicate operation: duplicates value of one of the general non-arithmetic registers into - /// another general non-arithmetic register of the same bit size, clearing its previous value. - #[display("dup {0}{1},{0}{2}")] - DupR(RegR, Reg32, Reg32), - - // ---- - /// Copy operation: copies value from one of the integer arithmetic registers to a destination - /// register treating value as unsigned: if the value does not fit destination bit dimension, - /// truncates the most significant bits until they fit, setting `st0` value to `false`. - /// Otherwise, the operation sets `st0` to `true`. - #[display("cpy {0}{1},{2}{3}")] - CpyA(RegA, Reg32, RegA, Reg32), - - /// Conversion operation: copies value from one of the integer arithmetic registers to a - /// destination register treating value as signed: if the value does not fit destination bit - /// dimension, truncates the most significant non-sign bits until they fit, setting `st0` - /// value to `false`. Otherwise, fills the difference between source and destination bit length - /// with the value taken from the most significant source bit (sign bit) and sets `st0` to - /// `true`. - #[display("cnv {0}{1},{2}{3}")] - CnvA(RegA, Reg32, RegA, Reg32), - - /// Conversion operation: converts value from one of the float arithmetic registers to a - /// destination register according to floating encoding rules. If the value does not fit - /// destination bit dimension, truncates the most significant non-sign bits until they fit, - /// setting `st0` value to `false`. Otherwise, sets `st0` to `true`. - #[display("cnv {0}{1},{2}{3}")] - CnvF(RegF, Reg32, RegF, Reg32), - - /// Copy operation: copies value from one of the general non-arithmetic registers to a - /// destination register. If the value does not fit destination bit dimension, - /// truncates the most significant bits until they fit, setting `st0` value to `false`. - /// Otherwise, extends most significant bits with zeros and sets `st0` to `true`. - #[display("cpy {0}{1},{2}{3}")] - CpyR(RegR, Reg32, RegR, Reg32), - - /// Swap-copy operation: swaps value one of the integer arithmetic registers with a value of a - /// general non-arithmetic register. If any of the values do not fit destination bit - /// dimensions, truncates the most significant bits until they fit, setting `st0` value to - /// `false`. Otherwise, extends most significant bits with zeros and sets `st0` to `true`. - #[display("spy {0}{1},{2}{3}")] - SpyAR(RegA, Reg32, RegR, Reg32), - - /// Conversion operation: converts value of an integer arithmetic register to a float register - /// according to floating encoding rules. If the value does not fit destination bit dimension, - /// truncates the most significant non-sign bits until they fit, setting `st0` value to - /// `false`. Otherwise, sets `st0` to `true`. - /// - /// NB: operation always treats integers as signed integers. - #[display("cnv {0}{1},{2}{3}")] - CnvAF(RegA, Reg32, RegF, Reg32), - - /// Conversion operation: converts value of a float arithmetic register to an integer register - /// according to floating encoding rules. If the value does not fit destination bit dimension, - /// truncates the most significant non-sign bits until they fit, setting `st0` value to - /// `false`. Otherwise, sets `st0` to `true`. - /// - /// NB: operation always treats integers as signed integers. - #[display("cnv {0}{1},{2}{3}")] - CnvFA(RegF, Reg32, RegA, Reg32), -} - -/// Instructions comparing register values -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum CmpOp { - /// Compares value of two integer arithmetic registers setting `st0` to `true` if the first - /// parameter is greater (and not equal) than the second one. If at least one of the registers - /// is set to `None`, sets `st0` to `false`. - #[display("gt.{0} {1}{2},{1}{3}")] - GtA(SignFlag, RegA, Reg32, Reg32), - - /// Compares value of two integer arithmetic registers setting `st0` to `true` if the first - /// parameter is lesser (and not equal) than the second one. If at least one of the registers - /// is set to `None`, sets `st0` to `false`. - #[display("lt.{0} {1}{2},{1}{3}")] - LtA(SignFlag, RegA, Reg32, Reg32), - - /// Compares value of two float arithmetic registers setting `st0` to `true` if the first - /// parameter is greater (and not equal) than the second one. If at least one of the registers - /// is set to `None`, sets `st0` to `false`. - #[display("gt.{0} {1}{2},{1}{3}")] - GtF(FloatEqFlag, RegF, Reg32, Reg32), - - /// Compares value of two float arithmetic registers setting `st0` to `true` if the first - /// parameter is lesser (and not equal) than the second one. If at least one of the registers - /// is set to `None`, sets `st0` to `false`. - #[display("lt.{0} {1}{2},{1}{3}")] - LtF(FloatEqFlag, RegF, Reg32, Reg32), - - // ---- - /// Compares value of two general non-arithmetic registers setting `st0` to `true` if the first - /// parameter is greater (and not equal) than the second one. If at least one of the registers - /// is set to `None`, sets `st0` to `false`. - #[display("gt {0}{1},{0}{2}")] - GtR(RegR, Reg32, Reg32), - - /// Compares value of two general non-arithmetic registers setting `st0` to `true` if the first - /// parameter is lesser (and not equal) than the second one. If at least one of the registers - /// is set to `None`, sets `st0` to `false`. - #[display("lt {0}{1},{0}{2}")] - LtR(RegR, Reg32, Reg32), - - /// Checks equality of value in two integer arithmetic (`A`) registers putting result into - /// `st0`. None-equality flag specifies value for `st0` for the cases when both of the - /// registers are in `None` state. - #[display("eq.{0} {1}{2},{1}{3}")] - EqA( - /** `st0` value if both of the registers are uninitialized */ NoneEqFlag, - RegA, - Reg32, - Reg32, - ), - - /// Checks equality of value in two float arithmetic (`F`) registers putting result into `st0`. - /// If both registers are `None`, the `st0` is set to `false`. - #[display("eq.{0} {1}{2},{1}{3}")] - EqF(FloatEqFlag, RegF, Reg32, Reg32), - - /// Checks equality of value in two non-arithmetic (`R`) registers putting result into `st0`. - /// None-equality flag specifies value for `st0` for the cases when both of the registers - /// are in `None` state. - #[display("eq.{0} {1}{2},{1}{3}")] - EqR( - /** `st0` value if both of the registers are uninitialized */ NoneEqFlag, - RegR, - Reg32, - Reg32, - ), - - // --- - /// Checks if the value in `A` register is equal to zero, setting `st0` to `true` in this case. - /// Otherwise, sets `st0` to false (including when the register is in the undefined state). - #[display("ifz {0}{1}")] - IfZA(RegA, Reg32), - - /// Checks if the value in `R` register is equal to zero, setting `st0` to `true` in this case. - /// Otherwise, sets `st0` to false (including when the register is in the undefined state). - #[display("ifz {0}{1}")] - IfZR(RegR, Reg32), - - /// Checks if the value in `A` register is in an undefined state, setting `st0` to `true` in - /// this case. Otherwise, sets `st0` to false. - #[display("ifn {0}{1}")] - IfNA(RegA, Reg32), - - /// Checks if the value in `R` register is in an undefined state, setting `st0` to `true` in - /// this case. Otherwise, sets `st0` to false. - #[display("ifn {0}{1}")] - IfNR(RegR, Reg32), - - /// Takes value from `st0` and merges into the value of the destination `A` register. The merge - /// operation is defined by the [`MergeFlag`] argument. - #[display("st.{0} {1}{2}")] - St(MergeFlag, RegA, Reg8), - - /// Inverses value in `st0` register - #[display("inv st0")] - StInv, -} - -/// Arithmetic instructions. -/// -/// All operations modify the value of `st0` register, setting it to `false` if the destination -/// is set to `None`. Otherwise, `st0` value is `true`, even if the overflow has occurred (when -/// `wrap` flag is provided). -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)] -pub enum ArithmeticOp { - /// Adds values from two integer arithmetic registers and puts result into the second register. - #[display("add.{0} {1}{2},{1}{3}")] - AddA(IntFlags, RegA, Reg32, Reg32), - - /// Adds values from two float arithmetic registers and puts result into the second register. - #[display("add.{0} {1}{2},{1}{3}")] - AddF(RoundingFlag, RegF, Reg32, Reg32), - - /// Subtracts the second register value from the first one and puts result into the second - /// register. - #[display("sub.{0} {1}{2},{1}{3}")] - SubA(IntFlags, RegA, Reg32, Reg32), - - /// Subtracts the second register value from the first one and puts result into the second - /// register. - #[display("sub.{0} {1}{2},{1}{3}")] - SubF(RoundingFlag, RegF, Reg32, Reg32), - - /// Multiplies values from two integer arithmetic registers and puts result into the second - /// register. - #[display("mul.{0} {1}{2},{1}{3}")] - MulA(IntFlags, RegA, Reg32, Reg32), - - /// Multiplies values from two float arithmetic registers and puts result into the second - /// register. - #[display("mul.{0} {1}{2},{1}{3}")] - MulF(RoundingFlag, RegF, Reg32, Reg32), - - /// Divides the first register value by the second one and puts result into the second - /// register. - /// - /// Since the division operation may not result in overflow, the overflow flag is used to - /// indicate rounding of the result: - /// - /// Overflow flag is also defines behaviour for zero division `(x/0 if x > 0)`: whether the - /// destination must be set to `0` (true) or to None (false). - /// - /// NB: Impossible arithmetic operation 0/0 always sets destination to `None`. - #[display("div.{0} {1}{2},{1}{3}")] - DivA(IntFlags, RegA, Reg32, Reg32), - - /// Divides the first register value by the second one and puts result into the second - /// register. - #[display("div.{0} {1}{2},{1}{3}")] - DivF(RoundingFlag, RegF, Reg32, Reg32), - - /// Modulo division. - /// - /// Puts a reminder of the division of the first register on the second register into the - /// second register. - #[display("rem {0}{1},{2}{3}")] - Rem(RegA, Reg32, RegA, Reg32), - - /// Increment/decrement register value on a given signed step. - /// - /// Sets the destination to `None` and `st0` to `false` in case of overflow. - #[display("{2:#} {0}{1},{2}")] - Stp(RegA, Reg32, Step), - - /// Negates most significant bit - #[display("neg {0}{1}")] - Neg(RegAF, Reg16), - - /// Replaces the register value with its absolute value - #[display("abs {0}{1}")] - Abs(RegAF, Reg16), -} - -/// Bit operations & boolean algebra instructions -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum BitwiseOp { - /// Bitwise AND operation - #[display("and {0}{1},{0}{2},{0}{3}")] - And(RegAR, /** Source 1 */ Reg16, /** Source 2 */ Reg16, /** Operation destination */ Reg16), - - /// Bitwise OR operation - #[display("or {0}{1},{0}{2},{0}{3}")] - Or(RegAR, /** Source 1 */ Reg16, /** Source 2 */ Reg16, /** Operation destination */ Reg16), - - /// Bitwise XOR operation - #[display("xor {0}{1},{0}{2},{0}{3}")] - Xor(RegAR, /** Source 1 */ Reg16, /** Source 2 */ Reg16, /** Operation destination */ Reg16), - - /// Bitwise inversion - #[display("not {0}{1}")] - Not(RegAR, Reg16), - - /// Left bit shift, filling added bits values with zeros. Sets `st0` value to the value of the - /// most significant bit before the operation. - /// - /// This, [`BitwiseOp::ShrA`] and [`BitwiseOp::ShrR`] operations are encoded with the same - /// instruction bitcode and differ only in their first two argument bits. - #[display("shl {0}{1},{2}{3}")] - Shl( - /** Which of `A` registers will have a shift value */ RegA2, - /** Index of `u8` or `u16` register with bitshift value */ Reg32, - /** Register to shift the value in */ RegAR, - /** Source & destination register */ Reg32, - ), - - /// Right bit shift for one of the integer arithmetic registers, filling added bits values with - /// zeros (if `sign` flag is set to `false`) or ones (if `sign` flag is set to `true`). - /// Sets `st0` value to the value of the least significant bit before the operation. - /// - /// This, [`BitwiseOp::Shl`] and [`BitwiseOp::ShrR`] operations are encoded with the same - /// instruction bitcode and differ only in their first two argument bits. - #[display("shr.{0} {1}{2},{3}{4}")] - ShrA( - /** Sign flag */ SignFlag, - /** Which of `A` registers will have a shift value */ RegA2, - /** Index of `u8` or `u16` register with bitshift value */ Reg16, - /** Family of `A` registers to shift */ RegA, - /** Source & destination `A` register */ Reg32, - ), - - /// Right bit shift for one of the general non-arithmetic registers, filling added bits values - /// with zeros (if `sign` flag is set to `false`) or ones (if `sign` flag is set to `true`). - /// Sets `st0` value to the value of the least significant bit before the operation. - /// - /// This, [`BitwiseOp::Shl`] and [`BitwiseOp::ShrA`] operations are encoded with the same - /// instruction bitcode and differ only in their first two argument bits. - #[display("shr {0}{1},{2}{3}")] - ShrR( - /** Which of `A` registers will have a shift value */ RegA2, - /** Index of `u8` or `u16` register with bitshift value */ Reg32, - /** Family of `R` registers to shift */ RegR, - /** Source & destination `R` register */ Reg32, - ), - - /// Left bit shift, cycling the shifted values (most significant bit becomes least - /// significant), putting the result into the first source register. Does not modify `st0` - /// value. - /// - /// This and the next [`BitwiseOp::Scr`] operation are encoded with the same instruction - /// bitcode and differ only in their first argument bit. - #[display("scl {0}{1},{2}{3}")] - Scl( - /** Which of `A` registers will have a shift value */ RegA2, - /** Index of `u8` or `u16` register with bitshift value */ Reg32, - /** Register to shift the value in */ RegAR, - /** Source & destination register */ Reg32, - ), - - /// Right bit shift, cycling the shifted values (the least significant bit becomes the most - /// significant), putting the result into the first source register. Does not modify `st0` - /// value. - /// - /// This and the previous [`BitwiseOp::Scl`] operation are encoded with the same instruction - /// bitcode and differ only in their first argument bit. - #[display("scr {0}{1},{2}{3}")] - Scr( - /** Which of `A` registers will have a shift value */ RegA2, - /** Index of `u8` or `u16` register with bitshift value */ Reg32, - /** Register to shift the value in */ RegAR, - /** Source & destination register */ Reg32, - ), - - /// Reverses bits order in the integer arithmetic register. Does not modify `st0` value. - #[display("rev {0}{1}")] - RevA(RegA, Reg32), - - /// Reverses bits order in the generic non-arithmetic register. Does not modify `st0` value. - #[display("rev {0}{1}")] - RevR(RegR, Reg32), -} - -/// Operations on byte strings. -/// -/// All of these operations either set `st0` to `false`, if an exception occurred during their -/// execution, or do not modify `st0` register value. Since each of the exceptions can be predicted -/// with a code run by VM beforehand (unlike for arithmetic exceptions), the absence of `st0` value -/// change upon success allows batching multiple string operations and checking their final result, -/// while still maintaining ability to predict/detect which of the operations has failed. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum BytesOp { - /// Put bytestring into a byte string register - /// - /// Data are kept in the separate data segment, thus when the instruction is parsed from the - /// code segment it knows only data offset and length. If this offset or length exceeds the - /// size of the data segment, the instruction truncates the string to the part that is present - /// in the data segment (or zero-length string if the offset exceeds data segment length) and - /// sets `st0` to `false`. Otherwise, `st0` is unaffected. - #[display("put {0},{1}")] - Put( - /** Destination `s` register index */ RegS, - Box, - /** Indicates that the operation must set `st0` to false; i.e. string data are not - * completely read from the data segment */ - bool, - ), - - /// Move bytestring value between registers - #[display("mov {0},{1}")] - Mov(/** Source `s` register index */ RegS, /** Destination `s` register index */ RegS), - - /// Swap bytestring value between registers - #[display("swp {0},{1}")] - Swp(/** First `s` register index */ RegS, /** Second `s` register index */ RegS), - - /// Fill segment of bytestring with specific byte value, setting the length of the string in - /// the destination register to specific value. - /// - /// The start offset is the least offset from one of the `a16` register provided in `offset` - /// arguments, the end offset is the greatest one. If any of the offsets exceeds the length of - /// the string in the destination register, operation behaviour is defined by the provided - /// boolean flag: - /// - if the flag is `true`, the string length is extended to the largest of the offsets and - /// all bytes between previous string length and start offset are filled with zeros, setting - /// `st0` value to `false`; - /// - if the flag is `false`, the destination register is set to `None` and `st0` is set to - /// `false`. - /// - /// If both of the offsets lie within the length of the string, the `st0` register value is not - /// modified. - /// - /// If any of the offsets or value registers are unset, sets `st0` to `false` and does not - /// change destination value. - #[display("fill.{4} {0},a16{1},a16{2},a8{3}")] - Fill( - /** `s` register index */ RegS, - /** `a16` register holding first offset */ Reg32, - /** `a16` register holding second offset (exclusive) */ Reg32, - /** `a8` register index holding the value */ Reg32, - /** Exception handling flag */ ExtendFlag, - ), - - /// Put length of the string into the destination register. - /// - /// If the string register is empty, or destination register can't fit the length, sets `st0` - /// to `false` and destination register to `None`. - #[display("len {0},{1}{2}")] - Len(/** `s` register index */ RegS, RegA, Reg32), - - /// Count number of byte occurrences from the `a8` register within the string and stores that - /// value into destination `a16` register. - /// - /// If the string register is empty, or the source byte value register is uninitialized, sets - /// `st0` to `false` and destination register to `None`. - #[display("cnt {0},a8{1},a16{2}")] - Cnt( - /** `s` register index */ RegS, - /** `a8` register with the byte value */ Reg16, - /** `a16` destination register index */ Reg16, - ), - - /// Check equality of two strings, putting result into `st0`. - /// - /// If both of strings are uninitialized, `st0` assigned `true` value. - #[display("eq {0},{1}")] - Eq(RegS, RegS), - - /// Compute offset and length of the `n`th fragment shared between two strings ("conjoint - /// fragment"), putting it to the destination `a16` registers. If strings have no conjoint - /// fragment sets destination to `None`. - #[display("con {0},{1},a16{2},a16{3},a16{4}")] - Con( - /** First source string register */ RegS, - /** Second source string register */ RegS, - /** Index of the conjoint fragment to match */ Reg32, - /** `a16` register index to save the offset of the conjoint fragment */ Reg32, - /** `a16` register index to save the length of the conjoint fragment */ Reg32, - ), - - /// Count number of occurrences of one string within another putting result to `a16[0]`, - /// - /// If the first or the second string is `None`, sets `st0` to `false` and `a16[0]` to `None`. - #[display("find a16[0],{0},{1}")] - Find(/** `s` register with string */ RegS, /** `s` register with matching fragment */ RegS), - - /// Extract byte string slice into general `r` register. The length of the extracted string is - /// equal to the bit dimension of the destination register. If the bit size of the destination - /// plus the initial offset exceeds string length the rest of the destination register bits is - /// filled with zeros and `st0` is set to `false`. Otherwise, `st0` value is not modified. - /// - /// If the source string register - or offset register is uninitialized, sets destination to - /// uninitialized state and `st0` to `false`. - #[display("extr {0},{1}{2},a16{3}")] - Extr(/** `s` register index */ RegS, RegAR, Reg16, /** `a16` register with offset */ Reg16), - - /// Inject general `R` register value at a given position to string register, replacing value - /// of the corresponding bytes. If the insert offset is larger than the current length of the - /// string, the length is extended and all bytes inbetween previous length and the new length - /// are initialized with zeros. If the length of the inserted string plus insert offset exceeds - /// the maximum string register length (2^16 bytes), then the destination register is set to - /// `None` state and `st0` is set to `false`. Otherwise, `st0` value is not modified. - #[display("inj {0},{1}{2},{1}{3}")] - Inj( - /** `s` register index acting as the source and destination */ RegS, - RegAR, - Reg16, - /** `a16` register with offset */ Reg16, - ), - - /// Join bytestrings from two registers into destination, overwriting its value. If the length - /// of the joined string exceeds the maximum string register length (2^16 bytes), then the - /// destination register is set to `None` state and `st0` is set to `false`. Otherwise, - /// `st0` value is not modified. - #[display("join {0},{1},{2}")] - Join(/** Source 1 */ RegS, /** Source 2 */ RegS, /** Destination */ RegS), - - /// Split bytestring at a given offset taken from `a16` register into two destination strings, - /// overwriting their value. If offset exceeds the length of the string in the register, - /// then the behaviour is determined by the [`SplitFlag`] value. - /// - ///
-    /// +--------------------
-    /// |       | ....
-    /// +--------------------
-    ///         ^       ^
-    ///         |       +-- Split offset (`offset`)
-    ///         +-- Source string length (`src_len`)
-    ///
-    /// `offset == 0`:
-    ///   (1) first, second <- None; `st0` <- false
-    ///   (2) first <- None, second <- `src_len > 0` ? src : None; `st0` <- false
-    ///   (3) first <- None, second <- `src_len > 0` ? src : zero-len; `st0` <- false
-    ///   (4) first <- zero-len, second <- `src_len > 0` ? src : zero-len
-    /// `offset > 0 && offset > src_len`: `st0` always set to false
-    ///   (1) first, second <- None
-    ///   (5) first <- short, second <- None
-    ///   (6) first <- short, second <- zero-len
-    ///   (7) first <- zero-ext, second <- None
-    ///   (8) first <- zero-ext, second <- zero-len
-    /// `offset = src_len`:
-    ///   (1) first, second <- None; `st0` <- false
-    ///   (5,7) first <- ok, second <- None; `st0` <- false
-    ///   (6,8) first <- ok, second <- zero-len
-    /// `offset < src_len`: operation succeeds anyway, `st0` value is not changed
-    /// 
- /// - /// Rule on `st0` changes: if at least one of the destination registers is set to `None`, or - /// `offset` value exceeds source string length, `st0` is set to `false`; otherwise its value - /// is not modified - #[display("splt.{0} {2},a16{1},{3},{4}")] - Splt( - SplitFlag, - /** `a16` register index with offset value */ Reg32, - /** Source */ RegS, - /** Destination 1 */ RegS, - /** Destination 2 */ RegS, - ), - - /// Insert value from one of bytestring register at a given index of other bytestring register, - /// shifting string bytes. If the destination register does not fit the length of the new - /// string, or the offset exceeds the length of destination string operation behaviour is - /// defined by the provided [`InsertFlag`]. - /// - ///
-    /// +--------------------
-    /// |       | ....
-    /// +--------------------
-    ///         ^       ^
-    ///         |       +-- Insert offset (`offset`)
-    ///         +-- Destination string length (`dst_len`)
-    ///
-    /// `offset < dst_len && src_len + dst_len > 2^16`:
-    ///   (6) Set destination to `None`
-    ///   (7) Cut destination string part exceeding `2^16`
-    ///   (8) Reduce `src_len` such that it will fit the destination
-    /// `offset > dst_len && src_len + dst_len + offset <= 2^16`:
-    ///   (1) Set destination to `None`
-    ///   (2) Fill destination from `dst_let` to `offset` with zeros
-    ///   (3) Use `src_len` instead of `offset`
-    /// `offset > dst_len && src_len + dst_len + offset > 2^16`:
-    ///   (4) Set destination to `None`
-    ///   (5) Fill destination from `dst_let` to `offset` with zeros and cut source string part
-    ///       exceeding `2^16`
-    ///   (6-8) Use `src_len` instead of `offset` and use flag value from the first section
-    /// 
- /// - /// In all of these cases `st0` is set to `false`. Otherwise, `st0` value is not modified. - #[display("ins.{0} {1},a16{2},{2}")] - Ins( - InsertFlag, - /** `a16` register index with offset value for insert location */ Reg32, - /** Source register */ RegS, - /** Destination register */ RegS, - ), - - /// Delete bytes in a given range, shifting the remaining bytes leftward. The start offset is - /// the least offset from one of the `a16` register provided in `offset` arguments, the end - /// offset is the greatest one. If any of the offsets exceeds the length of the string in - /// the destination register, operation behaviour is defined by the provided [`DeleteFlag`] - /// argument. - /// - ///
-    /// +----------------------------------
-    /// |                   | ....
-    /// +----------------------------------
-    ///     ^               ^       ^  
-    ///     |               |       +-- End offset (`offset_end`)
-    ///     |               +-- Source string length (`src_len`)
-    ///     +-- Start offset (`offset_start`)
-    ///
-    /// `offset_start > src_len`:
-    ///   (1) set destination to `None`
-    ///   (2) set destination to zero-length string
-    /// `offset_end > src_len && offset_start <= src_len`:
-    ///   (1) set destination to `None`
-    ///   (3) set destination to the fragment of the string `offset_start..src_len`
-    ///   (4) set destination to the fragment of the string `offset_start..src_len` and extend
-    ///       its length up to `offset_end - offset_start` with trailing zeros.
-    /// 
- /// - /// `flag1` and `flag2` arguments indicate whether `st0` should be set to `false` if - /// `offset_start > src_len` and `offset_end > src_len && offset_start <= src_len`. - /// In all other cases, `st0` value is not modified. - #[display("del.{0} {7},{8},{1}{2},{3}{4},{5},{6}")] - Del( - DeleteFlag, - RegA2, - /** `a8` or `a16` register index with a first offset for delete location */ Reg32, - RegA2, - /** `a8` or `a16` register index with a second offset for delete location */ Reg32, - /** `flag1` indicating `st0` value set to false if `offset_start > src_len` */ bool, - /** `flag2` indicating `st0` value set to false if - * `offset_end > src_len && offset_start <= src_len` */ - bool, - /** Source `s` register */ RegS, - /** Destination `s` register */ RegS, - ), - - /// Revert byte order of the string. - /// - /// If the source string register is uninitialized, resets destination to the uninitialized - /// state and sets `st0` to `false`. - #[display("rev {0},{1}")] - Rev(/** Source */ RegS, /** Destination */ RegS), -} - -/// Cryptographic hashing functions -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[non_exhaustive] -pub enum DigestOp { - /// Computes RIPEMD160 hash value. - /// - /// Sets `st0` to `false` and destination register to `None` if the source register does not - /// contain a value - #[display("ripemd {0},r160{1}")] - Ripemd( - /** Index of string register */ RegS, - /** Index of `r160` register to save result to */ Reg16, - ), - - /// Computes SHA256 hash value - /// - /// Sets `st0` to `false` and destination register to `None` if the source register does not - /// contain a value - #[display("sha2 {0},r256{1}")] - Sha256( - /** Index of string register */ RegS, - /** Index of `r256` register to save result to */ Reg16, - ), - - /// Computes SHA256 hash value - /// - /// Sets `st0` to `false` and destination register to `None` if the source register does not - /// contain a value - #[display("sha2 {0},r512{1}")] - Sha512( - /** Index of string register */ RegS, - /** Index of `r512` register to save result to */ Reg16, - ), - - /// Computes BLAKE3 hash value - /// - /// Sets `st0` to `false` and destination register to `None` if the source register does not - /// contain a value - #[display("blake3 {0},r256{1}")] - Blake3( - /** Index of string register */ RegS, - /** Index of `r256` register to save result to */ Reg16, - ), -} - -/// Operations on Secp256k1 elliptic curve -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -pub enum Secp256k1Op { - /// Generates new elliptic curve point value saved into destination - /// register in `r512` set using scalar value from the source `r256` - /// register - #[display("secpgen r256{0},r512{1}")] - Gen( - /** Register containing scalar */ Reg32, - /** Destination register to put G * scalar */ Reg8, - ), - - /// Multiplies elliptic curve point on a scalar - #[display("secpmul {0}256{1},r512{2},r512{3}")] - Mul( - /** Use `a` or `r` register as scalar source */ RegBlockAR, - /** Scalar register index */ Reg32, - /** Source `r` register index containing EC point */ Reg32, - /** Destination `r` register index */ Reg32, - ), - - /// Adds two elliptic curve points - #[display("secpadd r512{0},r512{1}")] - Add(/** Source 1 */ Reg32, /** Source 2 and destination */ Reg8), - - /// Negates elliptic curve point - #[display("secpneg r512{0},r512{1}")] - Neg(/** Register hilding EC point to negate */ Reg32, /** Destination register */ Reg8), -} - -/// Operations on Curve25519 elliptic curve -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -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},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},r256{2},r256{3}")] - Mul( - /** Use `a` or `r` register as scalar source */ RegBlockAR, - /** Scalar register index */ Reg32, - /** Source `r` register index containing EC point */ Reg32, - /** Destination `r` register index */ Reg32, - ), - - /// Adds two elliptic curve points - #[display("edadd r512{0},r256{1},r256{2},{3}")] - Add( - /** Source 1 */ Reg32, - /** Source 2 */ Reg32, - /** Destination register */ Reg32, - /** Allow overflows */ bool, - ), - - /// Negates elliptic curve point - #[display("edneg r256{0},r256{1}")] - Neg(/** Register hilding EC point to negate */ Reg32, /** Destination register */ Reg8), +/// Trait for instructions +pub trait Instruction: Display + Debug + Bytecode { + const ISA_EXT: &'static [&'static str]; + + type Core: CoreExt; + /// Context: external data which are accessible to the ISA. + type Context<'ctx>; + + fn isa_ext() -> TinyOrdSet { + let iter = Self::ISA_EXT.iter().copied().map(IsaId::from); + TinyOrdSet::from_iter_checked(iter) + } + + /// Lists all registers which are used by the instruction. + fn regs(&self) -> BTreeSet<::Reg> { + 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) -> BTreeSet<::Reg>; + + /// List of registers which value may be changed by the instruction. + fn dst_regs(&self) -> BTreeSet<::Reg>; + + /// The number of bytes in the source registers. + fn src_reg_bytes(&self) -> u16 { + self.src_regs() + .into_iter() + .map(::Reg::bytes) + .sum() + } + + /// The number of bytes in the destination registers. + fn dst_reg_bytes(&self) -> u16 { + self.dst_regs() + .into_iter() + .map(::Reg::bytes) + .sum() + } + + /// The size of the data coming as an instruction operands (i.e. except data coming from + /// registers or read from outside the instruction operands). + fn op_data_bytes(&self) -> u16; + + /// The size of the data read by the instruction from outside the registers (except data coming + /// as a parameter). + fn ext_data_bytes(&self) -> u16; + + fn base_complexity(&self) -> u64 { + (self.op_data_bytes() as u64 // 1k of complexity units per input bit + + self.src_reg_bytes() as u64 * 10 // 10k of complexity units per input bit + + self.dst_reg_bytes() as u64 * 10 // 10k of complexity units per output bit + + self.ext_data_bytes() as u64 * 100) // x10 complexity units per byte of external + // memory + * 8 // per bit + * 1000 // by default use large unit + } + + /// Returns computational complexity of the instruction. + /// + /// Computational complexity is the number of "CPU ticks" required to process the instruction. + fn complexity(&self) -> u64 { self.base_complexity() } + + /// Executes given instruction taking all registers as input and output. + /// + /// # Arguments + /// + /// The method is provided with the current code position which may be used by the instruction + /// for constructing call stack. + /// + /// # Returns + /// + /// Returns whether further execution should be stopped. + fn exec( + &self, + site: Site, + core: &mut Core, + context: &Self::Context<'_>, + ) -> ExecStep>; } diff --git a/src/isa/macros.rs b/src/isa/macros.rs deleted file mode 100644 index e07ca3c..0000000 --- a/src/isa/macros.rs +++ /dev/null @@ -1,1521 +0,0 @@ -// AluVM Assembler -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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::Vm; -/// # use aluvm::library::{Lib, LibSite}; -/// # 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 mut vm = Vm::::new(); -/// match vm.exec(LibSite::default(), |_| Some(&lib), &()) { -/// 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, RegAR, 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 @ $lib:literal ; $($tt:tt)* } => { - $code.push($crate::instr!{ $op $arg @ $lib }); - $crate::aluasm_inner! { $code => $( $tt )* } - }; - { $code:ident => $op:ident $arg:ident @ $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 $arglit:ident, $( $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 $arg:ident [ $idx:literal ] , $arglit:ident ; $($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) - }; - (test) => { - Instr::ControlFlow(ControlFlowOp::Test) - }; - (jmp $offset:literal) => { - Instr::ControlFlow(ControlFlowOp::Jmp($offset)) - }; - (jmp $offset:ident) => { - Instr::ControlFlow(ControlFlowOp::Jmp($offset)) - }; - (jif $offset:literal) => { - Instr::ControlFlow(ControlFlowOp::Jif($offset)) - }; - (jif $offset:ident) => { - Instr::ControlFlow(ControlFlowOp::Jif($offset)) - }; - (routine $offset:literal) => { - Instr::ControlFlow(ControlFlowOp::Routine($offset)) - }; - (routine $offset:ident) => { - Instr::ControlFlow(ControlFlowOp::Routine($offset)) - }; - (call $offset:literal @ $lib:ident) => { - Instr::ControlFlow(ControlFlowOp::Call(LibSite::with( - $offset, - $lib - ))) - }; - (call $offset:literal @ $lib:literal) => { - Instr::ControlFlow(ControlFlowOp::Call(LibSite::with( - $offset, - $lib.parse().expect("wrong library reference"), - ))) - }; - (exec $offset:literal @ $lib:ident) => { - Instr::ControlFlow(ControlFlowOp::Exec(LibSite::with( - $offset, - $lib - ))) - }; - (call $offset:ident @ $lib:ident) => { - Instr::ControlFlow(ControlFlowOp::Call(LibSite::with( - $offset, - $lib - ))) - }; - (exec $offset:literal @ $lib:literal) => { - Instr::ControlFlow(ControlFlowOp::Exec(LibSite::with( - $offset, - $lib.parse().expect("wrong library reference"), - ))) - }; - (exec $offset:ident @ $lib:ident) => { - Instr::ControlFlow(ControlFlowOp::Exec(LibSite::with( - $offset, - $lib - ))) - }; - (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], $reg:ident[$reg_idx:literal], a16[$offset_idx:literal]) => { - Instr::Bytes(BytesOp::Extr( - RegS::from($idx), - $crate::_reg_tyar!($reg), - $crate::_reg_idx16!($reg_idx), - $crate::_reg_idx16!($offset_idx), - )) - }; - (inj s16[$idx:literal], $reg:ident[$reg_idx:literal], a16[$offset_idx:literal]) => { - Instr::Bytes(BytesOp::Inj( - RegS::from($idx), - $crate::_reg_tyar!($reg), - $crate::_reg_idx16!($reg_idx), - $crate::_reg_idx16!($offset_idx), - )) - }; - (put s16[$idx:literal], $val:ident) => {{ - Instr::Bytes(BytesOp::Put(RegS::from($idx), Box::new(ByteStr::with(&$val)), false)) - }}; - (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))) - }}; - (put $reg:ident[$idx:literal], $val:ident) => {{ - let mut num = MaybeNumber::from($val); - 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), - )) - }}; - (putif $reg:ident[$idx:literal], $val:ident) => {{ - let mut num = MaybeNumber::from($val); - 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, $dst_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, $dst_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))) - }; - (blake3 s16[$idx1:literal],r256[$idx2:literal]) => { - Instr::Digest(DigestOp::Blake3(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_tyar { - (a8) => { - RegAR::A(RegA::A8) - }; - (a16) => { - RegAR::A(RegA::A16) - }; - (a32) => { - RegAR::A(RegA::A32) - }; - (a64) => { - RegAR::A(RegA::A64) - }; - (a128) => { - RegAR::A(RegA::A128) - }; - (a256) => { - RegAR::A(RegA::A256) - }; - (a512) => { - RegAR::A(RegA::A512) - }; - (a1024) => { - RegAR::A(RegA::A1024) - }; - - (r128) => { - RegAR::R(RegR::R128) - }; - (r160) => { - RegAR::R(RegR::R160) - }; - (r256) => { - RegAR::R(RegR::R256) - }; - (r512) => { - RegAR::R(RegR::R512) - }; - (r1024) => { - RegAR::R(RegR::R1024) - }; - (r2048) => { - RegAR::R(RegR::R2048) - }; - (r4096) => { - RegAR::R(RegR::R4096) - }; - (r8192) => { - RegAR::R(RegR::R8192) - }; -} - -#[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/masm.rs b/src/isa/masm.rs new file mode 100644 index 0000000..2c19e83 --- /dev/null +++ b/src/isa/masm.rs @@ -0,0 +1,240 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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::isa::Instr; +/// use aluvm::regs::Status; +/// use aluvm::{aluasm, Lib, LibId, LibSite, Vm}; +/// +/// let code = aluasm! { +/// nop ; +/// put CK, :ok ; +/// chk ; +/// }; +/// +/// let lib = Lib::assemble::>(&code).unwrap(); +/// let mut vm = Vm::>::new(); +/// match vm.exec(LibSite::new(lib.lib_id(), 0), &(), |_| Some(&lib)) { +/// Status::Ok => println!("success"), +/// Status::Fail => println!("failure"), +/// } +/// ``` +#[macro_export] +macro_rules! aluasm { + ($( $tt:tt )+) => {{ #[allow(unused_imports)] { + use $crate::isa::{Instr, CtrlInstr, ReservedInstr}; + $crate::aluasm_isa! { $( $tt )+ } + } }}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! aluasm_isa { + ($( $tt:tt )+) => {{ + let mut code: Vec> = vec![]; + #[allow(unreachable_code)] { + $crate::aluasm_inner! { code => $( $tt )+ } + } + code + }} +} + +#[doc(hidden)] +#[macro_export] +macro_rules! aluasm_inner { + // end of program + { $code:ident => } => { }; + // no operands + { $code:ident => $op:ident ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // special type + { $code:ident => $op:ident $reg:ident, :$val:ident ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $reg, :$val }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operand is an external jump to a named location in library literal + { $code:ident => $op:ident $arg:literal @ $lib:ident ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg @ $lib }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operand is an external jump to a position + { $code:ident => $op:ident $arg:literal @ $lib:literal #h ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg @ $lib #h }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operand is an external jump to a named location in named library + { $code:ident => $op:ident $arg:ident @ $lib:ident ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg @ $lib }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operands are all literals + { $code:ident => $op:ident $( $arg:literal ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operands are all idents + { $code:ident => $op:ident $( $arg:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operands are all local registries + { $code:ident => $op:ident $( $arg:ident : $idx:literal ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg : $idx ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operands are all argument registries + { $code:ident => $op:ident $( $arg:ident : $idx:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg : $idx ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operands are all saved registries + { $code:ident => $op:ident $( $arg:ident . $idx:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $( $arg . $idx ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + // operands are different types of registries + { $code:ident => $op:ident $arg:ident . $idx:literal, $( $args:ident : $idxs:literal ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg . $idx, $( $args : $idxs ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $arg:ident . $idx:literal, $( $args:ident : $idxs:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg . $idx, $( $args : $idxs ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $arg:ident : $idx:literal, $( $args:ident . $idxs:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg : $idx, $( $args . $idxs ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; + { $code:ident => $op:ident $arg:ident : $idx:ident, $( $args:ident . $idxs:ident ),+ ; $($tt:tt)* } => { + $code.push($crate::instr!{ $op $arg : $idx, $( $args . $idxs ),+ }); + $crate::aluasm_inner! { $code => $( $tt )* } + }; +} + +#[macro_export] +macro_rules! from_hex { + ($ty:ty, $val:literal) => { + $ty::from_str_radix(&stringify!($pos).expect("invalid hexadecimal literal")) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! instr { + (nop) => { + Instr::Ctrl(CtrlInstr::Nop) + }; + (chk) => { + Instr::Ctrl(CtrlInstr::Chk) + }; + (not CO) => { + Instr::Ctrl(CtrlInstr::NotCo) + }; + (put CK, :fail) => { + Instr::Ctrl(CtrlInstr::FailCk) + }; + (put CK, :ok) => { + Instr::Ctrl(CtrlInstr::RsetCk) + }; + (ret) => { + Instr::Ctrl(CtrlInstr::Ret) + }; + (stop) => { + Instr::Ctrl(CtrlInstr::Stop) + }; + + // Jumps + (jmp $pos:literal) => { + Instr::Ctrl(CtrlInstr::Jmp { pos: $pos }) + }; + (jmp $pos:literal #h) => { + Instr::Ctrl(CtrlInstr::Jmp { pos: from_hex!(u16, $pos) }) + }; + (jif CO, $pos:literal) => { + Instr::Ctrl(CtrlInstr::JiNe { pos: $pos }) + }; + (jif CO, $pos:literal #h) => { + Instr::Ctrl(CtrlInstr::JiNe { pos: from_hex!(u16, $pos) }) + }; + (jif CK, $pos:literal) => { + Instr::Ctrl(CtrlInstr::JiFail { pos: $pos }) + }; + (jif CK, $pos:literal #h) => { + Instr::Ctrl(CtrlInstr::JiFail { pos: from_hex!(u16, $pos) }) + }; + (jif +$shift:literal) => { + Instr::Ctrl(CtrlInstr::Sh { shift: $shift }) + }; + (jif +$shift:literal #h) => { + Instr::Ctrl(CtrlInstr::Sh { shift: from_hex!(i8, $shift) }) + }; + (jif -$shift:literal) => { + Instr::Ctrl(CtrlInstr::Sh { shift: $shift }) + }; + (jif -$shift:literal #h) => { + Instr::Ctrl(CtrlInstr::Sh { shift: from_hex!(i8, $shift) }) + }; + (jif CO, +$shift:literal) => { + Instr::Ctrl(CtrlInstr::ShNe { shift: $shift }) + }; + (jif CO, +$shift:literal #h) => { + Instr::Ctrl(CtrlInstr::ShNe { shift: from_hex!(i8, $shift) }) + }; + (jif CK, -$shift:literal) => { + Instr::Ctrl(CtrlInstr::ShFail { shift: $shift }) + }; + (jif CK, -$shift:literal #h) => { + Instr::Ctrl(CtrlInstr::ShFail { shift: from_hex!(i8, $shift) }) + }; + + // Calls + (jmp $lib:ident @ $pos:literal) => { + Instr::Ctrl(CtrlInstr::Exec { site: $crate::Site::new($lib, $pos) }) + }; + (jmp $lib:ident @ $pos:literal #h) => { + Instr::Ctrl(CtrlInstr::Exec { site: $crate::Site::new($lib, from_hex!(u16, $pos)) }) + }; + (call $lib:ident @ $pos:literal) => { + Instr::Ctrl(CtrlInstr::Call { site: $crate::Site::new($lib, $pos) }) + }; + (call $lib:ident @ $pos:literal #h) => { + Instr::Ctrl(CtrlInstr::Call { site: $crate::Site::new($lib, from_hex!(u16, $pos)) }) + }; + (call $pos:literal) => { + Instr::Ctrl(CtrlInstr::Fn { pos: $pos }) + }; + (call $pos:literal #h) => { + Instr::Ctrl(CtrlInstr::Fn { pos: from_hex!(u16, $pos) }) + }; + + { $($tt:tt)+ } => { + Instr::Reserved(isa_instr! { $( $tt )+ }) + }; +} diff --git a/src/isa/mod.rs b/src/isa/mod.rs index 3392b7e..56b3527 100644 --- a/src/isa/mod.rs +++ b/src/isa/mod.rs @@ -3,113 +3,35 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. -//! AluVM instruction set architecture +//! AluVM instruction set architecture. -#[macro_use] -mod macros; -mod bytecode; -mod exec; -mod flags; mod instr; -pub mod opcodes; - -pub use bytecode::{Bytecode, BytecodeError}; -pub use exec::{ExecStep, InstructionSet}; -pub use flags::{ - DeleteFlag, ExtendFlag, Flag, FloatEqFlag, InsertFlag, IntFlags, MergeFlag, NoneEqFlag, - ParseFlagError, RoundingFlag, SignFlag, SplitFlag, -}; -pub use instr::{ - ArithmeticOp, BitwiseOp, BytesOp, CmpOp, ControlFlowOp, Curve25519Op, DigestOp, Instr, MoveOp, - PutOp, ReservedOp, Secp256k1Op, -}; - -/// List of standardised ISA extensions. -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[non_exhaustive] -#[derive(Default)] -pub enum Isa { - /// Core ISA instruction set - #[display("ALU")] - #[default] - Alu, - - /// Floating-point operations - #[display("FLOAT")] - Float, - - /// Bitcoin-specific cryptographic hash functions - #[display("BPDIGEST")] - BpDigest, - - /// Operations on Secp256k1 curve - #[display("SECP256")] - Secp256k1, - - /// Operations on Curve25519 - #[display("ED25519")] - Curve25519, - - /// ALU runtime extensions - #[display("ALURE")] - AluRe, - - /// Bitcoin protocol-specific instructions - #[display("BP")] - Bp, - - /// RGB-specific instructions - #[display("RGB")] - Rgb, - - /// Lightning network protocol-specific instructions - #[display("LNP")] - Lnp, - - /// Instructions for SIMD - #[display("SIMD")] - Simd, +mod bytecode; +mod arch; - /// Instructions for biologically-inspired cognitive architectures - #[display("REBICA")] - Rebica, -} +mod ctrl; +mod masm; -impl Isa { - /// Enumerates all ISA extension variants - pub const fn all() -> [Isa; 11] { - [ - Isa::Alu, - Isa::Float, - Isa::BpDigest, - Isa::Secp256k1, - Isa::Curve25519, - Isa::AluRe, - Isa::Bp, - Isa::Rgb, - Isa::Lnp, - Isa::Simd, - Isa::Rebica, - ] - } -} +pub use arch::{Instr, IsaId, ReservedInstr, ISA_ID_MAX_LEN}; +pub use bytecode::{Bytecode, BytecodeRead, BytecodeWrite, CodeEofError}; +pub use ctrl::CtrlInstr; +pub use instr::{ExecStep, Instruction}; diff --git a/src/isa/opcodes.rs b/src/isa/opcodes.rs deleted file mode 100644 index ac5c1e1..0000000 --- a/src/isa/opcodes.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -#![allow(missing_docs)] -#![allow(clippy::unusual_byte_groupings)] - -// Control-flow instructions -pub const INSTR_FAIL: u8 = 0b00_000_000; -pub const INSTR_TEST: u8 = 0b00_000_001; -pub const INSTR_JMP: u8 = 0b00_000_010; -pub const INSTR_JIF: u8 = 0b00_000_011; -pub const INSTR_ROUTINE: u8 = 0b00_000_100; -pub const INSTR_CALL: u8 = 0b00_000_101; -pub const INSTR_EXEC: u8 = 0b00_000_110; -pub const INSTR_RET: u8 = 0b00_000_111; - -// Instructions setting register values -pub const INSTR_CLRA: u8 = 0b00_001_000; -pub const INSTR_CLRF: u8 = 0b00_001_001; -pub const INSTR_CLRR: u8 = 0b00_001_010; -pub const INSTR_PUTA: u8 = 0b00_001_011; -pub const INSTR_PUTF: u8 = 0b00_001_100; -pub const INSTR_PUTR: u8 = 0b00_001_101; -pub const INSTR_PUTIFA: u8 = 0b00_001_110; -pub const INSTR_PUTIFR: u8 = 0b00_001_111; - -// Instructions moving and swapping register values -pub const INSTR_MOV: u8 = 0b00_010_000; -pub const INSTR_CPA: u8 = 0b00_010_001; -pub const INSTR_CNA: u8 = 0b00_010_010; -pub const INSTR_CNF: u8 = 0b00_010_011; -pub const INSTR_CPR: u8 = 0b00_010_100; -pub const INSTR_SPY: u8 = 0b00_010_101; -pub const INSTR_CAF: u8 = 0b00_010_110; -pub const INSTR_CFA: u8 = 0b00_010_111; - -// Instructions comparing register values -pub const INSTR_LGT: u8 = 0b00_011_000; -pub const INSTR_CMP: u8 = 0b00_011_001; -pub const INSTR_IFZA: u8 = 0b00_011_010; -pub const INSTR_IFZR: u8 = 0b00_011_011; -pub const INSTR_IFNA: u8 = 0b00_011_100; -pub const INSTR_IFNR: u8 = 0b00_011_101; -pub const INSTR_ST: u8 = 0b00_011_110; -pub const INSTR_STINV: u8 = 0b00_011_111; - -// Arithmetic instructions -pub const INSTR_ADD: u8 = 0b00_100_000; -pub const INSTR_SUB: u8 = 0b00_100_001; -pub const INSTR_MUL: u8 = 0b00_100_010; -pub const INSTR_DIV: u8 = 0b00_100_011; -pub const INSTR_STP: u8 = 0b00_100_100; -pub const INSTR_NEG: u8 = 0b00_100_101; -pub const INSTR_ABS: u8 = 0b00_100_110; -pub const INSTR_REM: u8 = 0b00_100_111; - -// Bit operations & boolean algebra instructions -pub const INSTR_AND: u8 = 0b00_101_000; -pub const INSTR_OR: u8 = 0b00_101_001; -pub const INSTR_XOR: u8 = 0b00_101_010; -pub const INSTR_NOT: u8 = 0b00_101_011; -pub const INSTR_SHF: u8 = 0b00_101_100; -pub const INSTR_SHC: u8 = 0b00_101_101; -pub const INSTR_REVA: u8 = 0b00_101_110; -pub const INSTR_REVR: u8 = 0b00_101_111; - -// Operations on byte strings -pub const INSTR_PUT: u8 = 0b00_110_000; -pub const INSTR_MVS: u8 = 0b00_110_001; -pub const INSTR_SWP: u8 = 0b00_110_010; -pub const INSTR_FILL: u8 = 0b00_110_011; -pub const INSTR_LEN: u8 = 0b00_110_100; -pub const INSTR_CNT: u8 = 0b00_110_101; -pub const INSTR_EQ: u8 = 0b00_110_110; -pub const INSTR_CON: u8 = 0b00_110_111; - -pub const INSTR_FIND: u8 = 0b00_111_000; -pub const INSTR_EXTR: u8 = 0b00_111_001; -pub const INSTR_INJ: u8 = 0b00_111_010; -pub const INSTR_JOIN: u8 = 0b00_111_011; -pub const INSTR_SPLT: u8 = 0b00_111_100; -pub const INSTR_INS: u8 = 0b00_111_101; -pub const INSTR_DEL: u8 = 0b00_111_110; -pub const INSTR_REV: u8 = 0b00_111_111; - -// No-operation instruction -pub const INSTR_NOP: u8 = 0b11_111_111; - -// Reserved operations which can be used by future AluVM versions -pub const INSTR_RESV_FROM: u8 = 0b01_000_000; -pub const INSTR_RESV_TO: u8 = 0b01_111_111; - -// ## ISA extensions: - -// ### Hashing (BPDIGEST) - -pub const INSTR_RIPEMD: u8 = 0b10_000_000; -pub const INSTR_SHA256: u8 = 0b10_000_001; -pub const INSTR_SHA512: u8 = 0b10_000_010; -pub const INSTR_BLAKE3: u8 = 0b10_000_100; - -// ### Secp256k1 operations (SECP256K1) - -pub const INSTR_SECP_GEN: u8 = 0b10_001_000; -pub const INSTR_SECP_MUL: u8 = 0b10_001_001; -pub const INSTR_SECP_ADD: u8 = 0b10_001_010; -pub const INSTR_SECP_NEG: u8 = 0b10_001_011; - -// ### Curve25519 operations (ED25519) - -pub const INSTR_ED_GEN: u8 = 0b10_001_100; -pub const INSTR_ED_MUL: u8 = 0b10_001_101; -pub const INSTR_ED_ADD: u8 = 0b10_001_110; -pub const INSTR_ED_NEG: u8 = 0b10_001_111; - -// Opcodes with may be used by ISA extensions -pub const INSTR_ISAE_FROM: u8 = 0b10_000_000; -pub const INSTR_ISAE_TO: u8 = 0b11_111_110; diff --git a/src/lib.rs b/src/lib.rs index f8205e2..7d62e98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,25 +3,24 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. #![deny( non_upper_case_globals, @@ -30,10 +29,9 @@ unused_mut, unused_imports, dead_code, - missing_docs + // missing_docs )] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![cfg_attr(not(feature = "std"), no_std)] //! Rust implementation of AluVM (arithmetic logic unit virtual machine) and assembler from Alu //! Assembly language into bytecode. @@ -69,7 +67,7 @@ //! * Data segment is always signed; //! * Code commits to the used ISA extensions; //! * Libraries identified by the signature; -//! * Code does not runs if not all libraries are present; +//! * Code does not run if not all libraries are present; //! //! ![Comparison table](doc/comparison.png) //! @@ -80,7 +78,7 @@ //! //! ### Instruction opcodes //! -//! You will find all opcode implementation details documented in [`crate::isa::Instr`] API docs. +//! You will find all opcode implementation details documented in [`isa::Instr`] API docs. //! //! - RISC: only 256 instructions //! - 3 families of core instructions: @@ -134,44 +132,42 @@ //! - Call stack register (cs0), 3*2^16 bits (192kB block) //! - Call stack pointer register (cp0), 16 bits //! -//! [AluVM]: https://github.com/internet2-org/aluvm-spec +//! [AluVM]: https://github.com/AluVM/aluvm-spec #![allow(clippy::bool_assert_comparison)] -// TODO(#6) Complete string operations -// TODO(#7) Complete assembly compiler for string operations -// TODO(#8) Implement operations on Edwards curves - -#[cfg(not(any(feature = "alloc", feature = "std")))] -compile_error!("either `alloc` or `std` feature must be used"); - -#[macro_use] extern crate alloc; #[macro_use] extern crate amplify; #[macro_use] extern crate strict_encoding; +#[macro_use] +extern crate commit_verify; #[cfg(feature = "serde")] #[macro_use] -extern crate serde_crate as serde; -extern crate core; +extern crate serde; -pub mod data; +mod core; #[macro_use] pub mod isa; -pub mod library; -pub mod reg; +mod library; +mod vm; #[cfg(feature = "stl")] pub mod stl; -mod vm; -pub use isa::Isa; -#[cfg(feature = "ascii-armor")] -pub use library::LibArmorError; +pub mod regs { + pub use crate::core::{Status, CALL_STACK_SIZE_MAX}; +} + +pub use isa::{ExecStep, IsaId, ISA_ID_MAX_LEN}; +#[cfg(feature = "armor")] +pub use library::armor::LibArmorError; +pub use library::{Lib, LibId, LibSite}; #[doc(hidden)] pub use paste::paste; pub use vm::Vm; -/// Struct types library name. +pub use self::core::{Core, CoreConfig, CoreExt, NoExt, NoRegs, Register, Site, SiteId}; + pub const LIB_NAME_ALUVM: &str = "AluVM"; diff --git a/src/library/armor.rs b/src/library/armor.rs new file mode 100644 index 0000000..9335bbb --- /dev/null +++ b/src/library/armor.rs @@ -0,0 +1,78 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use ::armor::{ArmorHeader, ArmorParseError, AsciiArmor, ASCII_ARMOR_ID}; +use amplify::confinement::{self, Confined, U24 as U24MAX}; +use strict_encoding::{DeserializeError, StrictDeserialize, StrictSerialize}; + +use super::*; + +const ASCII_ARMOR_ISAE: &str = "ISA-Extensions"; +const ASCII_ARMOR_DEPENDENCY: &str = "Dependency"; + +/// Errors while deserializing lib-old from an ASCII Armor. +#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[display(inner)] +pub enum LibArmorError { + /// Armor parse error. + #[from] + Armor(ArmorParseError), + + /// The provided data exceed maximum possible lib-old size. + #[from(confinement::Error)] + TooLarge, + + /// Library data deserialization error. + #[from] + Decode(DeserializeError), +} + +impl AsciiArmor for Lib { + type Err = LibArmorError; + const PLATE_TITLE: &'static str = "ALUVM LIB"; + + fn ascii_armored_headers(&self) -> Vec { + let mut headers = vec![ + ArmorHeader::new(ASCII_ARMOR_ID, self.lib_id().to_string()), + ArmorHeader::new(ASCII_ARMOR_ISAE, self.isae_string()), + ]; + for dep in &self.libs { + headers.push(ArmorHeader::new(ASCII_ARMOR_DEPENDENCY, dep.to_string())); + } + headers + } + + fn to_ascii_armored_data(&self) -> Vec { + self.to_strict_serialized::() + .expect("type guarantees") + .to_vec() + } + + fn with_headers_data(_headers: Vec, data: Vec) -> Result { + // TODO: check id, dependencies and ISAE + let data = Confined::try_from(data)?; + let me = Self::from_strict_serialized::(data)?; + Ok(me) + } +} diff --git a/src/library/assembler.rs b/src/library/assembler.rs new file mode 100644 index 0000000..c8e3d1c --- /dev/null +++ b/src/library/assembler.rs @@ -0,0 +1,94 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use amplify::confinement::{self, TinyOrdSet}; + +use super::{Lib, LibId, MarshallError, Marshaller}; +use crate::isa::{BytecodeRead, CodeEofError, Instruction}; + +/// Errors while assembling lib-old from the instruction set. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Display, Error, From)] +#[display(inner)] +pub enum AssemblerError { + /// Error assembling code and data segments. + #[from] + Bytecode(MarshallError), + + /// Error assembling library segment. + #[from] + LibSegOverflow(confinement::Error), +} + +impl Lib { + /// Assembles library from the provided instructions by encoding them into bytecode. + pub fn assemble(code: &[Isa]) -> Result + where Isa: Instruction { + let call_sites = code.iter().filter_map(|instr| instr.external_ref()); + let libs_segment = TinyOrdSet::try_from_iter(call_sites)?; + + let mut writer = Marshaller::new(&libs_segment); + for instr in code.iter() { + instr.encode_instr(&mut writer)?; + } + let (code_segment, data_segment) = writer.finish(); + + Ok(Lib { + isae: Isa::isa_ext(), + libs: libs_segment, + code: code_segment, + data: data_segment, + }) + } + + /// Disassembles library into a set of instructions. + pub fn disassemble(&self) -> Result, CodeEofError> + where Isa: Instruction { + let mut code = Vec::new(); + let mut reader = Marshaller::with(&self.code, &self.data, &self.libs); + while !reader.is_eof() { + code.push(Isa::decode_instr(&mut reader)?); + } + 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 std::io::Write, + ) -> Result<(), std::io::Error> + where + Isa: Instruction, + { + let mut reader = Marshaller::with(&self.code, &self.data, &self.libs); + while !reader.is_eof() { + let pos = reader.offset().0 as usize; + write!(writer, "@x{pos:06X}: ")?; + match Isa::decode_instr(&mut reader) { + Ok(instr) => writeln!(writer, "{instr}")?, + Err(_) => writeln!(writer, "; ")?, + } + } + Ok(()) + } +} diff --git a/src/library/constants.rs b/src/library/constants.rs deleted file mode 100644 index ad744cc..0000000 --- a/src/library/constants.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -//! Constants defined for AluVM libraries - -#![allow(missing_docs)] - -pub const CODE_SEGMENT_MAX_LEN: usize = 0xFFFF; - -pub const DATA_SEGMENT_MAX_LEN: usize = 0xFFFF; - -/// Maximum number of libraries that may be referenced (used by) any other library; i.e. limit for -/// the number of records inside program segment. -pub const LIBS_SEGMENT_MAX_COUNT: usize = 0xFF; - -/// Maximum total number of libraries which may be used by a single program; i.e. maximal number of -/// nodes in a library dependency tree. -pub const LIBS_MAX_TOTAL: u16 = 1024; - -pub const ISAE_SEGMENT_MAX_COUNT: usize = 64; - -pub const ISA_ID_MIN_LEN: usize = 2; - -pub const ISA_ID_MAX_LEN: usize = 8; - -pub const ISA_ID_ALU: &str = "ALU"; -pub const ISA_ID_BPDIGEST: &str = "BPDIGEST"; -pub const ISA_ID_SECP256K: &str = "SECP256K"; -pub const ISA_ID_ED25519: &str = "ED25519"; - -pub const ISA_ID_ALURE: &str = "ALURE"; -pub const ISA_ID_SIMD: &str = "SIMD"; -pub const ISA_ID_INET2: &str = "INET4"; -pub const ISA_ID_WEB4: &str = "WEB4"; - -pub const ISA_ID_BITCOIN: &str = "BITCOIN"; -pub const ISA_ID_BP: &str = "BP"; -pub const ISA_ID_RGB: &str = "RGB"; -pub const ISA_ID_LNP: &str = "LNP"; - -pub const ISA_ID_REBICA: &str = "REBICA"; diff --git a/src/library/cursor.rs b/src/library/cursor.rs deleted file mode 100644 index db35e11..0000000 --- a/src/library/cursor.rs +++ /dev/null @@ -1,568 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -use core::convert::TryInto; -#[cfg(feature = "std")] -use core::fmt::{self, Debug, Display, Formatter}; - -use amplify::num::{u1, u2, u24, u3, u4, u5, u6, u7}; - -use super::{CodeEofError, LibId, LibSeg, Read, Write, WriteError}; -use crate::data::Number; -use crate::isa::{Bytecode, Instr, InstructionSet}; -use crate::library::constants::{CODE_SEGMENT_MAX_LEN, DATA_SEGMENT_MAX_LEN}; -use crate::reg::NumericRegister; - -/// Cursor for accessing bytecode bounded by [`CODE_SEGMENT_MAX_LEN`] length and data segment -/// bounded by [`DATA_SEGMENT_MAX_LEN`] -pub struct Cursor<'a, T, D> -where - T: AsRef<[u8]>, - D: AsRef<[u8]>, - Self: 'a, -{ - bytecode: T, - bit_pos: u3, - byte_pos: u16, - data: D, - libs: &'a LibSeg, -} - -#[cfg(feature = "std")] -impl<'a, T, D> Debug for Cursor<'a, T, D> -where - T: AsRef<[u8]>, - D: AsRef<[u8]>, - Self: 'a, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use amplify::hex::ToHex; - f.debug_struct("Cursor") - .field("bytecode", &self.as_ref().to_hex()) - .field("byte_pos", &self.byte_pos) - .field("bit_pos", &self.bit_pos) - .field("data", &self.data.as_ref().to_hex()) - .field("program", &self.libs) - .finish() - } -} - -#[cfg(feature = "std")] -impl<'a, T, D> Display for Cursor<'a, T, D> -where - T: AsRef<[u8]>, - D: AsRef<[u8]>, - Self: 'a, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use amplify::hex::ToHex; - write!(f, "{}:{} @ ", self.byte_pos, self.bit_pos)?; - let hex = self.as_ref().to_hex(); - if f.alternate() { - write!(f, "{}..{}", &hex[..4], &hex[hex.len() - 4..]) - } else { - f.write_str(&hex) - } - } -} - -impl<'a, T, D> Cursor<'a, T, D> -where - T: AsRef<[u8]>, - D: AsRef<[u8]> + Default, - Self: 'a, -{ - /// Creates new cursor able to write the bytecode and data, using provided immutable program - /// segment - #[inline] - pub fn new(bytecode: T, libs: &'a LibSeg) -> Cursor<'a, T, D> { - Cursor { - bytecode, - byte_pos: 0, - bit_pos: u3::MIN, - data: D::default(), - libs, - } - } -} - -impl<'a, T, D> Cursor<'a, T, D> -where - T: AsRef<[u8]>, - D: AsRef<[u8]>, - Self: 'a, -{ - /// Creates cursor from the provided byte string utilizing existing program segment - /// - /// # Panics - /// - /// If the length of the bytecode exceeds [`CODE_SEGMENT_MAX_LEN`] or length of the data - /// [`DATA_SEGMENT_MAX_LEN`] - #[inline] - pub fn with(bytecode: T, data: D, libs: &'a LibSeg) -> Cursor<'a, T, D> { - assert!(bytecode.as_ref().len() <= CODE_SEGMENT_MAX_LEN); - assert!(data.as_ref().len() <= DATA_SEGMENT_MAX_LEN); - 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 } - - #[inline] - fn as_ref(&self) -> &[u8] { self.bytecode.as_ref() } - - fn read(&mut self, bit_count: u5) -> Result { - let mut ret = 0u32; - let mut cnt = bit_count.to_u8(); - while cnt > 0 { - if self.is_eof() { - return Err(CodeEofError); - } - let byte = self.as_ref()[self.byte_pos as usize]; - let remaining_bits = 8 - self.bit_pos.to_u8(); - let mask = match remaining_bits < cnt { - true => 0xFFu8 << self.bit_pos.to_u8(), - false => (((1u16 << (cnt)) - 1) << (self.bit_pos.to_u8() as u16)) as u8, - }; - let value = ((byte & mask) >> self.bit_pos.to_u8()) as u32; - ret |= value << (bit_count.to_u8() - cnt); - match remaining_bits.min(cnt) { - 8 => { - self.inc_bytes(1)?; - } - _ => { - self.inc_bits(u3::with(remaining_bits.min(cnt)))?; - } - } - cnt = cnt.saturating_sub(remaining_bits); - } - Ok(ret) - } - - fn inc_bits(&mut self, bit_count: u3) -> Result<(), CodeEofError> { - let pos = self.bit_pos.to_u8() + bit_count.to_u8(); - self.bit_pos = u3::with(pos % 8); - self._inc_bytes_inner(pos as u16 / 8) - } - - fn inc_bytes(&mut self, byte_count: u16) -> Result<(), CodeEofError> { - assert_eq!( - self.bit_pos.to_u8(), - 0, - "attempt to access (multiple) bytes at a non-byte aligned position" - ); - self._inc_bytes_inner(byte_count) - } - - #[inline] - fn _inc_bytes_inner(&mut self, byte_count: u16) -> Result<(), CodeEofError> { - self.byte_pos = self.byte_pos.checked_add(byte_count).ok_or(CodeEofError)?; - Ok(()) - } -} - -impl<'a, T, D> Cursor<'a, T, D> -where - T: AsRef<[u8]> + AsMut<[u8]>, - D: AsRef<[u8]>, - Self: 'a, -{ - fn as_mut(&mut self) -> &mut [u8] { self.bytecode.as_mut() } - - fn write(&mut self, value: u32, bit_count: u5) -> Result<(), CodeEofError> { - let mut cnt = bit_count.to_u8(); - let value = ((value as u64) << (self.bit_pos.to_u8())).to_le_bytes(); - let n_bytes = (cnt + self.bit_pos.to_u8() + 7) / 8; - for i in 0..n_bytes { - if self.is_eof() { - return Err(CodeEofError); - } - let byte_pos = self.byte_pos as usize; - let bit_pos = self.bit_pos.to_u8(); - let byte = &mut self.as_mut()[byte_pos]; - *byte |= value[i as usize]; - match (bit_pos, cnt) { - (0, cnt) if cnt >= 8 => { - self.inc_bytes(1)?; - } - (_, cnt) => { - self.inc_bits(u3::with(cnt.min(8 - bit_pos)))?; - } - } - cnt = cnt.saturating_sub(cnt.min(8 - bit_pos)); - } - Ok(()) - } -} - -impl<'a, T, D> Cursor<'a, T, D> -where - T: AsRef<[u8]> + AsMut<[u8]>, - D: AsRef<[u8]> + AsMut<[u8]> + Extend, - Self: 'a, -{ - fn write_unique(&mut self, bytes: &[u8]) -> Result { - // We write the value only if the value is not yet present in the data segment - let len = bytes.len(); - let offset = self.data.as_ref().len(); - if len == 0 { - Ok(offset as u16) - } else if let Some(offset) = - self.data.as_ref().windows(len).position(|window| window == bytes) - { - Ok(offset as u16) - } else if offset + len > DATA_SEGMENT_MAX_LEN { - Err(WriteError::DataNotFittingSegment) - } else { - self.data.extend(bytes.iter().copied()); - Ok(offset as u16) - } - } -} - -impl<'a, T, D> Read for Cursor<'a, T, D> -where - T: AsRef<[u8]>, - D: AsRef<[u8]>, - Self: 'a, -{ - #[inline] - fn is_eof(&self) -> bool { self.byte_pos as usize >= self.as_ref().len() } - - #[inline] - fn pos(&self) -> u16 { self.byte_pos } - - #[inline] - fn seek(&mut self, byte_pos: u16) -> Result { - if byte_pos as usize >= self.as_ref().len() { - return Err(CodeEofError); - } - let old_pos = self.byte_pos; - self.byte_pos = byte_pos; - Ok(old_pos) - } - - fn peek_u8(&self) -> Result { - if self.is_eof() { - return Err(CodeEofError); - } - Ok(self.as_ref()[self.byte_pos as usize]) - } - - fn read_bool(&mut self) -> Result { Ok(self.read(u5::with(1))? == 0x01) } - - fn read_u1(&mut self) -> Result { - let res = self.read(u5::with(1))? as u8; - Ok(res.try_into().expect("bit extractor failure")) - } - - fn read_u2(&mut self) -> Result { - let res = self.read(u5::with(2))? as u8; - Ok(res.try_into().expect("bit extractor failure")) - } - - fn read_u3(&mut self) -> Result { - let res = self.read(u5::with(3))? as u8; - Ok(res.try_into().expect("bit extractor failure")) - } - - fn read_u4(&mut self) -> Result { - let res = self.read(u5::with(4))? as u8; - Ok(res.try_into().expect("bit extractor failure")) - } - - fn read_u5(&mut self) -> Result { - let res = self.read(u5::with(5))? as u8; - Ok(res.try_into().expect("bit extractor failure")) - } - - fn read_u6(&mut self) -> Result { - let res = self.read(u5::with(6))? as u8; - Ok(res.try_into().expect("bit extractor failure")) - } - - fn read_u7(&mut self) -> Result { - let res = self.read(u5::with(7))? as u8; - Ok(res.try_into().expect("bit extractor failure")) - } - - fn read_u8(&mut self) -> Result { - let res = self.read(u5::with(8))? as u8; - Ok(res) - } - - fn read_i8(&mut self) -> Result { - let res = self.read(u5::with(8))? as i8; - Ok(res) - } - - fn read_u16(&mut self) -> Result { - let res = self.read(u5::with(16))? as u16; - Ok(res) - } - - fn read_i16(&mut self) -> Result { - let res = self.read(u5::with(16))? as i16; - Ok(res) - } - - fn read_u24(&mut self) -> Result { - let res = self.read(u5::with(24))?; - Ok(res.try_into().expect("bit extractor failure")) - } - - #[inline] - fn read_lib(&mut self) -> Result { - Ok(self.libs.at(self.read_u8()?).unwrap_or_default()) - } - - fn read_data(&mut self) -> Result<(&[u8], bool), CodeEofError> { - let offset = self.read_u16()? as usize; - let end = offset + self.read_u16()? as usize; - let max = DATA_SEGMENT_MAX_LEN; - let st0 = end > self.data.as_ref().len(); - let data = &self.data.as_ref()[offset.min(max)..end.min(max)]; - Ok((data, st0)) - } - - fn read_number(&mut self, reg: impl NumericRegister) -> Result { - let offset = self.read_u16()? as usize; - let end = offset + reg.bytes() as usize; - if end > self.data.as_ref().len() { - return Err(CodeEofError); - } - Ok(Number::with(&self.data.as_ref()[offset..end], reg.layout()) - .expect("read_number is broken")) - } -} - -impl<'a, T, D> Write for Cursor<'a, T, D> -where - T: AsRef<[u8]> + AsMut<[u8]>, - D: AsRef<[u8]> + AsMut<[u8]> + Extend, - Self: 'a, -{ - fn write_bool(&mut self, data: bool) -> Result<(), WriteError> { - self.write(data as u32, u5::with(1)).map_err(WriteError::from) - } - - fn write_u1(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().into_u8() as u32, u5::with(1)).map_err(WriteError::from) - } - - fn write_u2(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().to_u8() as u32, u5::with(2)).map_err(WriteError::from) - } - - fn write_u3(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().to_u8() as u32, u5::with(3)).map_err(WriteError::from) - } - - fn write_u4(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().to_u8() as u32, u5::with(4)).map_err(WriteError::from) - } - - fn write_u5(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().to_u8() as u32, u5::with(5)).map_err(WriteError::from) - } - - fn write_u6(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().to_u8() as u32, u5::with(6)).map_err(WriteError::from) - } - - fn write_u7(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().to_u8() as u32, u5::with(7)).map_err(WriteError::from) - } - - fn write_u8(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into() as u32, u5::with(8)).map_err(WriteError::from) - } - - fn write_i8(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into() as u32, u5::with(8)).map_err(WriteError::from) - } - - fn write_u16(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into() as u32, u5::with(16)).map_err(WriteError::from) - } - - fn write_i16(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into() as u32, u5::with(16)).map_err(WriteError::from) - } - - fn write_u24(&mut self, data: impl Into) -> Result<(), WriteError> { - self.write(data.into().into_u32(), u5::with(24)).map_err(WriteError::from) - } - - #[inline] - fn write_lib(&mut self, lib: LibId) -> Result<(), WriteError> { - self.write_u8(self.libs.index(lib).ok_or(WriteError::LibAbsent(lib))?) - } - - fn write_data(&mut self, bytes: impl AsRef<[u8]>) -> Result<(), WriteError> { - // We control that `self.byte_pos + bytes.len() < u16` at buffer - // allocation time, so if we panic here this means we have a bug in - // out allocation code and has to kill the process and report this issue - let bytes = bytes.as_ref(); - let len = bytes.len(); - if len >= u16::MAX as usize { - return Err(WriteError::DataExceedsLimit(len)); - } - let offset = self.write_unique(bytes)?; - self.write_u16(offset)?; - self.write_u16(len as u16) - } - - fn write_number( - &mut self, - reg: impl NumericRegister, - mut value: Number, - ) -> 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.reshape(reg.layout().using_sign(value.layout())); - let offset = self.write_unique(&value[..])?; - self.write_u16(offset) - } - - fn edit(&mut self, pos: u16, editor: F) -> Result<(), E> - where - F: FnOnce(&mut Instr) -> Result<(), E>, - E: From, - S: InstructionSet, - { - let prev_pos = self.seek(pos)?; - let mut instr = Instr::decode(self)?; - editor(&mut instr)?; - self.seek(pos)?; - instr.encode(self).expect("cursor editor fail"); - self.seek(prev_pos)?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use amplify::num::{u2, u3, u5, u7}; - - use super::Cursor; - use crate::data::{ByteStr, Number}; - use crate::library::{LibSeg, Read, Write}; - use crate::reg::RegA; - - #[test] - fn read() { - let libseg = LibSeg::default(); - let mut cursor = Cursor::<_, ByteStr>::new([0b01010111, 0b00001001], &libseg); - assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); - assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000001); - assert_eq!(cursor.read_u8().unwrap(), 0b10010101); - - let mut cursor = Cursor::<_, ByteStr>::new([0b01010111, 0b00001001], &libseg); - assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); - assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000101); - assert_eq!(cursor.read_u8().unwrap(), 0b01001010); - - let mut cursor = Cursor::<_, ByteStr>::new([0b01110111, 0b00001111], &libseg); - assert_eq!(cursor.read_u8().unwrap(), 0b01110111); - assert_eq!(cursor.read_u3().unwrap().to_u8(), 0b00000111); - assert_eq!(cursor.read_u5().unwrap().to_u8(), 0b00000001); - - let bytes = 0b11101011_11110000_01110111; - let mut cursor = Cursor::<_, ByteStr>::new(u32::to_le_bytes(bytes), &libseg); - assert_eq!(cursor.read(u5::with(24)).unwrap(), bytes); - } - - #[test] - fn read_eof() { - let libseg = LibSeg::default(); - let mut cursor = Cursor::<_, ByteStr>::new([0b01010111], &libseg); - assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000011); - assert_eq!(cursor.read_u2().unwrap().to_u8(), 0b00000001); - assert!(cursor.read_u8().is_err()); - } - - #[test] - fn write() { - let libseg = LibSeg::default(); - 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(); - cursor.write_u7(u7::with(0b01011111)).unwrap(); - cursor.write_u8(0b11100111).unwrap(); - cursor.write_bool(true).unwrap(); - 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 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!(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] - fn write_eof() { - let libseg = LibSeg::default(); - let mut code = [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(); - cursor.write_u7(u7::with(0b01011111)).unwrap(); - assert!(cursor.write_u8(0b11100111).is_err()); - } -} diff --git a/src/library/exec.rs b/src/library/exec.rs new file mode 100644 index 0000000..92aaa2f --- /dev/null +++ b/src/library/exec.rs @@ -0,0 +1,174 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +#[cfg(feature = "log")] +use baid64::DisplayBaid64; + +use super::{Lib, Marshaller}; +use crate::isa::{Bytecode, BytecodeRead, ExecStep, Instruction}; +use crate::{Core, LibId, LibSite, Site}; + +impl Lib { + /// Execute library code starting at entrypoint. + /// + /// # Returns + /// + /// Location for the external code jump, if any. + pub fn exec( + &self, + entrypoint: u16, + registers: &mut Core, + context: &Instr::Context<'_>, + ) -> Option + where + Instr: Instruction + Bytecode, + { + #[cfg(feature = "log")] + 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 marshaller = Marshaller::with(&self.code, &self.data, &self.libs); + let lib_id = self.lib_id(); + + #[cfg(feature = "log")] + let lib_mnemonic = lib_id.to_baid64_mnemonic(); + #[cfg(feature = "log")] + let lib_ref = lib_mnemonic.split_at(5).0; + + if marshaller.seek(entrypoint).is_err() { + registers.reset_ck(); + #[cfg(feature = "log")] + eprintln!("jump to non-existing offset; halting, {d}st0{z} is set to {r}false{z}"); + return None; + } + + #[cfg(feature = "log")] + let mut ck0 = registers.ck(); + + while !marshaller.is_eof() { + let pos = marshaller.pos(); + + let instr = Instr::decode_instr(&mut marshaller).ok()?; + + #[cfg(feature = "log")] + { + eprint!("{m}{}@x{pos:06X}:{z} {: <32}; ", lib_ref, instr.to_string()); + for reg in instr.src_regs() { + eprint!("{d}{reg} {z}"); + if let Some(val) = registers.get(reg) { + eprint!("{w}{}{z}, ", val); + } else { + eprint!("{g}~{z}, "); + } + } + } + + let next = instr.exec(Site::new(lib_id, pos), registers, context); + + #[cfg(feature = "log")] + { + eprint!("-> "); + for reg in instr.dst_regs() { + eprint!("{g}{reg} {z}"); + if let Some(val) = registers.get(reg) { + eprint!("{y}{}{z}, ", val); + } else { + eprint!("{g}~{z}, "); + } + } + if ck0 != registers.ck() { + let c = if registers.ck().is_ok() { g } else { r }; + eprint!(" {d}CK {z}{c}{}{z}, ", registers.ck()); + } + + ck0 = registers.ck(); + } + + if !registers.acc_complexity(instr.complexity()) { + #[cfg(feature = "log")] + eprintln!("complexity overflow"); + return None; + } + match next { + ExecStep::Stop => { + #[cfg(feature = "log")] + { + let c = if registers.ck().is_ok() { g } else { r }; + eprintln!("execution stopped; {d}CK {z}{c}{}{z}", registers.ck()); + } + return None; + } + ExecStep::FailHalt => { + let _ = registers.fail_ck(); + #[cfg(feature = "log")] + eprintln!("halting, {d}CK{z} is set to {r}false{z}"); + return None; + } + ExecStep::Next => { + #[cfg(feature = "log")] + eprintln!(); + continue; + } + ExecStep::FailContinue => { + if registers.fail_ck() { + #[cfg(feature = "log")] + eprintln!( + "halting, {d}CK{z} is set to {r}false{z} and {d}ch{z} is {r}true{z}" + ); + return None; + } + #[cfg(feature = "log")] + eprintln!("failing, {d}CK{z} is set to {r}false{z}"); + continue; + } + ExecStep::Jump(pos) => { + #[cfg(feature = "log")] + eprintln!("{}", pos); + if marshaller.seek(pos).is_err() { + let _ = registers.fail_ck(); + #[cfg(feature = "log")] + eprintln!( + "jump to non-existing offset; halting, {d}CK{z} is set to {r}fail{z}" + ); + return None; + } + } + ExecStep::Call(site) => { + #[cfg(feature = "log")] + eprintln!("{}", site); + return Some(site.into()); + } + } + } + + None + } +} diff --git a/src/library/lib.rs b/src/library/lib.rs index 9dafaf7..cf5dffc 100644 --- a/src/library/lib.rs +++ b/src/library/lib.rs @@ -3,73 +3,58 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::cmp::Ordering; -use core::fmt::{self, Display, Formatter}; -use core::hash::{Hash as RustHash, Hasher}; +use core::fmt; +use core::fmt::{Display, Formatter}; use core::str::FromStr; -use amplify::confinement::SmallBlob; -use amplify::{confinement, ByteArray, Bytes32}; +use amplify::confinement::{SmallBlob, TinyOrdSet}; +use amplify::Bytes32; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use sha2::{Digest, Sha256}; -#[cfg(feature = "std")] +use commit_verify::{CommitId, CommitmentId, Digest, Sha256}; use strict_encoding::{StrictDeserialize, StrictSerialize}; -#[cfg(feature = "ascii-armor")] -pub use self::_armor::LibArmorError; -use super::{Cursor, Read, WriteError}; -use crate::data::ByteStr; -#[cfg(feature = "std")] -use crate::isa::{Bytecode, Instr}; -use crate::isa::{BytecodeError, ExecStep, InstructionSet}; -use crate::library::segs::IsaSeg; -use crate::library::{CodeEofError, LibSeg, SegmentError}; -use crate::reg::CoreRegs; -use crate::LIB_NAME_ALUVM; +use crate::core::SiteId; +use crate::{IsaId, Site, LIB_NAME_ALUVM}; -pub const LIB_ID_TAG: [u8; 32] = *b"urn:ubideco:aluvm:lib:v01#230304"; +pub const LIB_ID_TAG: &str = "urn:ubideco:aluvm:lib:v01#241020"; -/// Unique identifier for a library. +/// Unique identifier for an AluVM library. #[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Debug, From)] #[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)] -#[derive(StrictType, StrictDecode)] -#[cfg_attr(feature = "std", derive(StrictEncode))] +#[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_ALUVM)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))] pub struct LibId( #[from] #[from([u8; 32])] Bytes32, ); +impl SiteId for LibId {} + +impl CommitmentId for LibId { + const TAG: &'static str = LIB_ID_TAG; +} + impl DisplayBaid64 for LibId { const HRI: &'static str = "alu"; const CHUNKING: bool = true; @@ -87,458 +72,120 @@ impl Display for LibId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) } } -impl LibId { - /// Computes LibId from the provided data - pub fn with( - isae: impl AsRef, - code: impl AsRef<[u8]>, - data: impl AsRef<[u8]>, - libs: &LibSeg, - ) -> LibId { - let mut tagger = Sha256::default(); - tagger.update(LIB_ID_TAG); - let tag = tagger.finalize(); - - let mut hasher = Sha256::default(); - hasher.update(tag); - hasher.update(tag); - - let isae = isae.as_ref(); - let code = code.as_ref(); - let data = data.as_ref(); - hasher.update((isae.len() as u8).to_le_bytes()); - hasher.update(isae.as_bytes()); - hasher.update((code.len() as u16).to_le_bytes()); - hasher.update(code); - hasher.update((data.len() as u16).to_le_bytes()); - hasher.update(data); - hasher.update([libs.count()]); - for lib in libs { - hasher.update(lib.as_slice()); - } - - LibId::from_byte_array(hasher.finalize()) - } +impl From for LibId { + fn from(hash: Sha256) -> Self { Self(Bytes32::from_byte_array(hash.finalize())) } } -/// AluVM executable code library -#[derive(Clone, Debug, Default)] -#[derive(StrictType, StrictDecode)] -#[cfg_attr(feature = "std", derive(StrictEncode))] +/// Location inside the instruction sequence which can be executed by the core. +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_ALUVM)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct Lib { - /// ISA segment - pub isae: IsaSeg, - /// Code segment - pub code: SmallBlob, - /// Data segment - pub data: SmallBlob, - /// Libs segment - pub libs: LibSeg, -} - -#[cfg(feature = "std")] -impl StrictSerialize for Lib {} -#[cfg(feature = "std")] -impl StrictDeserialize for Lib {} - -impl Display for Lib { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln!(f, "ISAE: {}", &self.isae)?; - write!(f, "CODE:\n{:#10}", ByteStr::with(self.code.as_ref()))?; - write!(f, "DATA:\n{:#10}", ByteStr::with(self.data.as_ref()))?; - if self.libs.count() > 0 { - write!(f, "LIBS: {:8}", self.libs) - } else { - write!(f, "LIBS: none") - } - } -} - -impl PartialEq for Lib { - #[inline] - fn eq(&self, other: &Self) -> bool { self.id().eq(&other.id()) } -} - -impl Eq for Lib {} - -impl PartialOrd for Lib { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))] +pub struct LibSite { + pub lib_id: LibId, + pub offset: u16, } -impl Ord for Lib { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { self.id().cmp(&other.id()) } +impl From> for LibSite { + fn from(site: Site) -> Self { Self { lib_id: site.prog_id, offset: site.offset } } } -impl RustHash for Lib { +impl LibSite { #[inline] - fn hash(&self, state: &mut H) { state.write(&self.id()[..]) } -} - -#[cfg(feature = "ascii-armor")] -mod _armor { - use amplify::confinement::{self, Confined, U24 as U24MAX}; - use armor::{ArmorHeader, ArmorParseError, AsciiArmor, ASCII_ARMOR_ID}; - use strict_encoding::DeserializeError; - - use super::*; - - const ASCII_ARMOR_ISAE: &str = "ISA-Extensions"; - const ASCII_ARMOR_DEPENDENCY: &str = "Dependency"; - - /// Errors while deserializing library from an ASCII Armor. - #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] - #[display(inner)] - pub enum LibArmorError { - /// Armor parse error. - #[from] - Armor(ArmorParseError), - - /// The provided data exceed maximum possible library size. - #[from(confinement::Error)] - TooLarge, - - /// Library data deserialization error. - #[from] - Decode(DeserializeError), - } - - impl AsciiArmor for Lib { - type Err = LibArmorError; - const PLATE_TITLE: &'static str = "ALUVM LIB"; - - fn ascii_armored_headers(&self) -> Vec { - let mut headers = vec![ - ArmorHeader::new(ASCII_ARMOR_ID, self.id().to_string()), - ArmorHeader::new(ASCII_ARMOR_ISAE, self.isae.to_string()), - ]; - for dep in &self.libs { - headers.push(ArmorHeader::new(ASCII_ARMOR_DEPENDENCY, dep.to_string())); - } - headers - } - - fn to_ascii_armored_data(&self) -> Vec { - self.to_strict_serialized::().expect("type guarantees").to_vec() - } - - fn with_headers_data(_headers: Vec, data: Vec) -> Result { - // TODO: check id, dependencies and ISAE - let data = Confined::try_from(data)?; - let me = Self::from_strict_serialized::(data)?; - Ok(me) - } - } + pub fn new(lib_id: LibId, offset: u16) -> Self { LibSite { lib_id, offset } } } -/// Errors while assembling library from the instruction set -#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Display, From)] -#[display(inner)] -pub enum AssemblerError { - /// Error assembling code and data segments - #[from] - #[from(WriteError)] - Bytecode(BytecodeError), +pub type LibsSeg = TinyOrdSet; - /// Error assembling library segment - #[from] - LibSegOverflow(confinement::Error), +#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_ALUVM)] +#[derive(CommitEncode)] +#[commit_encode(id = LibId, strategy = strict)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Lib { + pub isae: TinyOrdSet, + pub code: SmallBlob, + pub data: SmallBlob, + pub libs: LibsSeg, } -#[cfg(feature = "std")] -impl ::std::error::Error for AssemblerError { - fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { - match self { - AssemblerError::Bytecode(err) => Some(err), - AssemblerError::LibSegOverflow(err) => Some(err), - } - } -} +impl StrictSerialize for Lib {} +impl StrictDeserialize for Lib {} impl Lib { - /// Constructs library from raw data split into segments - pub fn with( - isa: &str, - bytecode: Vec, - data: Vec, - libs: LibSeg, - ) -> Result { - let isae = IsaSeg::from_str(isa)?; - let len = bytecode.len(); - Ok(Self { - isae, - libs, - code: SmallBlob::try_from(bytecode) - .map_err(|_| SegmentError::CodeSegmentTooLarge(len))?, - data: SmallBlob::try_from(data).map_err(|_| SegmentError::DataSegmentTooLarge(len))?, - }) - } - - /// Assembles library from the provided instructions by encoding them into bytecode - pub fn assemble(code: &[Isa]) -> Result - where Isa: InstructionSet { - let call_sites = code.iter().filter_map(|instr| instr.call_site()).map(|site| site.lib); - let libs_segment = LibSeg::try_from_iter(call_sites)?; - - let mut code_segment = ByteStr::default(); - let mut writer = Cursor::<_, ByteStr>::new(&mut code_segment.bytes[..], &libs_segment); - for instr in code.iter() { - instr.encode(&mut writer)?; - } - let pos = writer.pos(); - let data_segment = SmallBlob::from_checked(writer.into_data_segment().to_vec()); - code_segment.adjust_len(pos); - let code_segment = SmallBlob::from_checked(code_segment.to_vec()); - - Ok(Lib { - isae: Isa::isa_ids(), - libs: libs_segment, - code: code_segment, - data: data_segment, - }) - } - - /// Disassembles library into a set of instructions - pub fn disassemble(&self) -> Result, CodeEofError> - where Isa: InstructionSet { - let mut code = Vec::new(); - let mut reader = Cursor::with(&self.code, &self.data, &self.libs); - while !reader.is_eof() { - code.push(Isa::decode(&mut reader)?); - } - Ok(code) - } - - /// Disassembles library into a set of instructions and offsets and prints it to the writer. - #[cfg(feature = "std")] - pub fn print_disassemble( - &self, - mut writer: impl std::io::Write, - ) -> Result<(), std::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, "@x{pos:06X}: ")?; - match Instr::::decode(&mut reader) { - Ok(instr) => writeln!(writer, "{instr}")?, - Err(_) => writeln!(writer, "\n{}", ByteStr::with(&self.code.as_ref()[pos..]))?, - } - } - Ok(()) + pub fn lib_id(&self) -> LibId { self.commit_id() } + + pub fn isae_string(&self) -> String { + self.isae + .iter() + .map(IsaId::to_string) + .collect::>() + .join(" ") } +} - /// 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, - /// data). - #[inline] - pub fn id(&self) -> LibId { - LibId::with(self.isae_segment(), &self.code, &self.data, &self.libs) - } - - /// Returns ISA data - #[inline] - pub fn isae_segment(&self) -> String { self.isae.to_string() } - - /// Returns reference to code segment - #[inline] - pub fn code_segment(&self) -> &[u8] { self.code.as_ref() } - - /// Returns reference to data segment - #[inline] - pub fn data_segment(&self) -> &[u8] { self.data.as_ref() } - - /// Returns reference to libraries segment - #[inline] - pub fn libs_segment(&self) -> &LibSeg { &self.libs } - - /// Executes library code starting at entrypoint - /// - /// # Returns - /// - /// Location for the external code jump, if any - pub fn exec( - &self, - entrypoint: u16, - registers: &mut CoreRegs, - context: &Isa::Context<'_>, - ) -> Option - where - Isa: InstructionSet, - { - #[cfg(feature = "log")] - 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_id = self.id(); - - #[cfg(feature = "log")] - let lib_mnemonic = lib_id.to_baid64_mnemonic(); - #[cfg(feature = "log")] - let lib_ref = lib_mnemonic.split_at(5).0; - - if cursor.seek(entrypoint).is_err() { - registers.st0 = false; - #[cfg(feature = "log")] - eprintln!("jump to non-existing offset; halting, {d}st0{z} is set to {r}false{z}"); - return None; - } - - #[cfg(feature = "log")] - let mut st0 = registers.st0; - - while !cursor.is_eof() { - let pos = cursor.pos(); - - let instr = Isa::decode(&mut cursor).ok()?; - - #[cfg(feature = "log")] - { - eprint!("{m}{}@x{pos:06X}:{z} {: <32}; ", lib_ref, 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_id), context); - - #[cfg(feature = "log")] - { - 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(feature = "log")] - eprintln!("complexity overflow"); - return None; - } - match next { - ExecStep::Stop => { - #[cfg(feature = "log")] - { - let c = if registers.st0 { g } else { r }; - eprintln!("execution stopped; {d}st0={z}{c}{}{z}", registers.st0); - } - return None; - } - ExecStep::Fail => { - registers.st0 = false; - assert_eq!(registers.st0, false); - #[cfg(feature = "log")] - eprintln!("halting, {d}st0{z} is set to {r}false{z}"); - return None; - } - ExecStep::Next => { - #[cfg(feature = "log")] - eprintln!(); - continue; - } - ExecStep::Jump(pos) => { - #[cfg(feature = "log")] - eprintln!("{}", pos); - if cursor.seek(pos).is_err() { - registers.st0 = false; - #[cfg(feature = "log")] - eprintln!( - "jump to non-existing offset; halting, {d}st0{z} is set to {r}false{z}" - ); - return None; - } - } - ExecStep::Call(site) => { - #[cfg(feature = "log")] - eprintln!("{}", site); - return Some(site); - } - } +impl Display for Lib { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "ISAE: {}", self.isae_string())?; + writeln!(f, "CODE: {:x}", self.code)?; + writeln!(f, "DATA: {:x}", self.data)?; + if self.libs.len() > 0 { + writeln!( + f, + "LIBS: {:8}", + self.libs + .iter() + .map(LibId::to_string) + .collect::>() + .join("\n ") + ) + } else { + writeln!(f, "LIBS: ~") } - - None } } -/// Location within a library -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, Display)] -#[derive(StrictType, StrictDecode)] -#[strict_type(lib = LIB_NAME_ALUVM)] -#[cfg_attr(feature = "std", derive(StrictEncode))] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -#[display("{pos} @ {lib}")] -pub struct LibSite { - /// Library hash - pub lib: LibId, - - /// Offset from the beginning of the code, in bytes - pub pos: u16, -} - -impl LibSite { - /// Constricts library site reference from a given position and library hash - /// value - pub fn with(pos: u16, lib: LibId) -> LibSite { LibSite { lib, pos } } -} - #[cfg(test)] mod test { + use strict_encoding::StrictDumb; + use super::*; #[test] fn lib_id_display() { - let id = LibId::with("FLOAT", b"", b"", &none!()); + let id = Lib::strict_dumb().lib_id(); assert_eq!( format!("{id}"), - "alu:650XHPmh-WpXWR5R-Uz4B5jX-jeDqcyr-HXpdZxY-aX9gfO4#plasma-tunnel-mama" + "alu:uZkzX1J9-i5EvGTf-J1TB79p-OBvKq5x-1U2n4qd-8Nso3Ag#reunion-cable-tractor" ); assert_eq!( format!("{id:-}"), - "650XHPmh-WpXWR5R-Uz4B5jX-jeDqcyr-HXpdZxY-aX9gfO4#plasma-tunnel-mama" + "uZkzX1J9-i5EvGTf-J1TB79p-OBvKq5x-1U2n4qd-8Nso3Ag#reunion-cable-tractor" ); - assert_eq!(format!("{id:#}"), "alu:650XHPmh-WpXWR5R-Uz4B5jX-jeDqcyr-HXpdZxY-aX9gfO4"); - assert_eq!(format!("{id:-#}"), "650XHPmh-WpXWR5R-Uz4B5jX-jeDqcyr-HXpdZxY-aX9gfO4"); + assert_eq!(format!("{id:#}"), "alu:uZkzX1J9-i5EvGTf-J1TB79p-OBvKq5x-1U2n4qd-8Nso3Ag"); + assert_eq!(format!("{id:-#}"), "uZkzX1J9-i5EvGTf-J1TB79p-OBvKq5x-1U2n4qd-8Nso3Ag"); } #[test] fn lib_id_from_str() { - let id = LibId::with("FLOAT", b"", b"", &none!()); + let id = Lib::strict_dumb().lib_id(); assert_eq!( id, LibId::from_str( - "alu:650XHPmh-WpXWR5R-Uz4B5jX-jeDqcyr-HXpdZxY-aX9gfO4#plasma-tunnel-mama" + "alu:uZkzX1J9-i5EvGTf-J1TB79p-OBvKq5x-1U2n4qd-8Nso3Ag#reunion-cable-tractor" ) .unwrap() ); - assert_eq!(id, LibId::from_str("alu:650XHPmhWpXWR5RUz4B5jXjeDqcyrHXpdZxYaX9gfO4").unwrap()); + assert_eq!(id, LibId::from_str("alu:uZkzX1J9i5EvGTfJ1TB79pOBvKq5x1U2n4qd8Nso3Ag").unwrap()); assert_eq!( id, - LibId::from_str("alu:650XHPmhWpXWR5RUz4B5jXjeDqcyrHXpdZxYaX9gfO4#plasma-tunnel-mama") - .unwrap() + LibId::from_str( + "alu:uZkzX1J9i5EvGTfJ1TB79pOBvKq5x1U2n4qd8Nso3Ag#reunion-cable-tractor" + ) + .unwrap() ); - assert_eq!(id, LibId::from_str("650XHPmhWpXWR5RUz4B5jXjeDqcyrHXpdZxYaX9gfO4").unwrap()); + assert_eq!(id, LibId::from_str("uZkzX1J9i5EvGTfJ1TB79pOBvKq5x1U2n4qd8Nso3Ag").unwrap()); } } diff --git a/src/library/marshaller.rs b/src/library/marshaller.rs new file mode 100644 index 0000000..de0f193 --- /dev/null +++ b/src/library/marshaller.rs @@ -0,0 +1,528 @@ +// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). +// To find more on AluVM please check +// +// SPDX-License-Identifier: Apache-2.0 +// +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky +// +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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. + +use core::fmt::{self, Debug, Formatter}; + +use amplify::confinement::SmallBlob; +use amplify::num::{u1, u2, u3, u4, u5, u6, u7}; + +use super::{LibId, LibsSeg}; +use crate::isa::{BytecodeRead, BytecodeWrite, CodeEofError}; + +/// Errors write operations +#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum MarshallError { + /// attempt to read or write outside of code segment (i.e. at position > 0xFF). + #[from(CodeEofError)] + CodeNotFittingSegment, + + /// data size {0} exceeds limit of 0xFF bytes. + DataExceedsLimit(usize), + + /// attempt to write data which does not fit code segment. + DataNotFittingSegment, + + /// attempt to write library reference for the lib id {0} which is not a part of program + /// segment. + LibAbsent(LibId), +} + +/// Marshals instructions to and from bytecode representation. +pub struct Marshaller<'a, C, D> +where + C: AsRef<[u8]>, + D: AsRef<[u8]>, + Self: 'a, +{ + bit_pos: u3, + byte_pos: u16, + bytecode: C, + data: D, + libs: &'a LibsSeg, +} + +impl<'a, C, D> Debug for Marshaller<'a, C, D> +where + C: AsRef<[u8]>, + D: AsRef<[u8]>, + Self: 'a, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Marshaller") + .field("bytecode", &SmallBlob::from_slice_checked(self.bytecode.as_ref())) + .field("byte_pos", &self.byte_pos) + .field("bit_pos", &self.bit_pos) + .field("data", &SmallBlob::from_slice_checked(self.data.as_ref())) + .field("libs", &self.libs) + .finish() + } +} + +impl<'a> Marshaller<'a, Vec, Vec> +where Self: 'a +{ + /// Creates a new marshaller using provided set of libraries. + #[inline] + pub fn new(libs: &'a LibsSeg) -> Self { + Self { + bytecode: default!(), + byte_pos: 0, + bit_pos: u3::MIN, + data: default!(), + libs, + } + } + + /// Completes marshalling, returning produced data segment. + /// + /// # Panics + /// + /// If marshaller position is not at byte margin. + #[inline] + pub fn finish(self) -> (SmallBlob, SmallBlob) { + if self.bit_pos != u3::ZERO { + panic!("incomplete marshalling") + } + (SmallBlob::from_checked(self.bytecode), SmallBlob::from_checked(self.data)) + } +} + +impl<'a> Marshaller<'a, SmallBlob, SmallBlob> +where Self: 'a +{ + #[cfg(test)] + pub fn into_code_data(self) -> (SmallBlob, SmallBlob) { (self.bytecode, self.data) } +} + +impl<'a, C, D> Marshaller<'a, C, D> +where + C: AsRef<[u8]>, + D: AsRef<[u8]>, + Self: 'a, +{ + /// Create marshaller from byte string utilizing existing bytecode. + /// + /// # Panics + /// + /// If the length of the bytecode or data segment exceeds 0xFF. + #[inline] + pub fn with(bytecode: C, data: D, libs: &'a LibsSeg) -> Self { + Self { bytecode, byte_pos: 0, bit_pos: u3::MIN, data, libs } + } + + /// Returns the current offset of the marshaller + pub const fn offset(&self) -> (u16, u3) { (self.byte_pos, self.bit_pos) } + + fn read(&mut self, bit_count: u5) -> Result { + let mut ret = 0u32; + let mut cnt = bit_count.to_u8(); + while cnt > 0 { + if self.is_eof() { + return Err(CodeEofError); + } + let byte = &self.bytecode.as_ref()[self.byte_pos as usize]; + let remaining_bits = 8 - self.bit_pos.to_u8(); + let mask = match remaining_bits < cnt { + true => 0xFFu8 << self.bit_pos.to_u8(), + false => (((1u16 << (cnt)) - 1) << (self.bit_pos.to_u8() as u16)) as u8, + }; + let value = ((byte & mask) >> self.bit_pos.to_u8()) as u32; + ret |= value << (bit_count.to_u8() - cnt); + match remaining_bits.min(cnt) { + 8 => { + self.inc_bytes(1)?; + } + _ => { + self.inc_bits(u3::with(remaining_bits.min(cnt)))?; + } + } + cnt = cnt.saturating_sub(remaining_bits); + } + Ok(ret) + } + + fn inc_bits(&mut self, bit_count: u3) -> Result<(), CodeEofError> { + let pos = self.bit_pos.to_u8() + bit_count.to_u8(); + self.bit_pos = u3::with(pos % 8); + self._inc_bytes_inner(pos as u16 / 8) + } + + fn inc_bytes(&mut self, byte_count: u16) -> Result<(), CodeEofError> { + assert_eq!( + self.bit_pos.to_u8(), + 0, + "attempt to access (multiple) bytes at a non-byte aligned position" + ); + self._inc_bytes_inner(byte_count) + } + + #[inline] + fn _inc_bytes_inner(&mut self, byte_count: u16) -> Result<(), CodeEofError> { + self.byte_pos = self.byte_pos.checked_add(byte_count).ok_or(CodeEofError)?; + Ok(()) + } +} + +impl<'a, C, D> Marshaller<'a, C, D> +where + C: AsRef<[u8]> + AsMut<[u8]> + Extend, + D: AsRef<[u8]>, + Self: 'a, +{ + fn write(&mut self, value: u32, bit_count: u5) -> Result<(), CodeEofError> { + let mut cnt = bit_count.to_u8(); + let value = ((value as u64) << (self.bit_pos.to_u8())).to_le_bytes(); + let n_bytes = (cnt + self.bit_pos.to_u8() + 7) / 8; + for i in 0..n_bytes { + if self.bytecode.as_ref().len() >= u16::MAX as usize { + return Err(CodeEofError); + } + if self.is_eof() { + self.bytecode.extend([0]); + } + let byte_pos = self.byte_pos as usize; + let bit_pos = self.bit_pos.to_u8(); + let byte = &mut self.bytecode.as_mut()[byte_pos]; + *byte |= value[i as usize]; + match (bit_pos, cnt) { + (0, cnt) if cnt >= 8 => { + self.inc_bytes(1)?; + } + (_, cnt) => { + self.inc_bits(u3::with(cnt.min(8 - bit_pos)))?; + } + } + cnt = cnt.saturating_sub(cnt.min(8 - bit_pos)); + } + Ok(()) + } +} + +impl<'a, C, D> Marshaller<'a, C, D> +where + C: AsRef<[u8]> + AsMut<[u8]>, + D: AsRef<[u8]> + AsMut<[u8]> + Extend, + Self: 'a, +{ + fn write_unique(&mut self, bytes: &[u8]) -> Result { + // We write the value only if the value is not yet present in the data segment + let len = bytes.len(); + let offset = self.data.as_ref().len(); + if len == 0 { + Ok(offset as u16) + } else if let Some(offset) = self + .data + .as_ref() + .windows(len) + .position(|window| window == bytes) + { + Ok(offset as u16) + } else if offset + len > u16::MAX as usize { + Err(MarshallError::DataNotFittingSegment) + } else { + self.data.extend(bytes.iter().copied()); + Ok(offset as u16) + } + } +} + +impl<'a, C, D> BytecodeRead for Marshaller<'a, C, D> +where + C: AsRef<[u8]>, + D: AsRef<[u8]>, + Self: 'a, +{ + #[inline] + fn pos(&self) -> u16 { self.byte_pos } + + #[inline] + fn seek(&mut self, byte_pos: u16) -> Result { + if byte_pos as usize >= self.bytecode.as_ref().len() { + return Err(CodeEofError); + } + let old_pos = self.byte_pos; + self.byte_pos = byte_pos; + Ok(old_pos) + } + + #[inline] + fn is_eof(&self) -> bool { self.byte_pos as usize >= self.bytecode.as_ref().len() } + + fn peek_byte(&self) -> Result { + if self.is_eof() { + return Err(CodeEofError); + } + Ok(self.bytecode.as_ref()[self.byte_pos as usize]) + } + + fn read_bool(&mut self) -> Result { Ok(self.read(u5::with(1))? == 0x01) } + + fn read_1bit(&mut self) -> Result { + let res = self.read(u5::with(1))? as u8; + Ok(res.try_into().expect("bit extractor failure")) + } + + fn read_2bits(&mut self) -> Result { + let res = self.read(u5::with(2))? as u8; + Ok(res.try_into().expect("bit extractor failure")) + } + + fn read_3bits(&mut self) -> Result { + let res = self.read(u5::with(3))? as u8; + Ok(res.try_into().expect("bit extractor failure")) + } + + fn read_4bits(&mut self) -> Result { + let res = self.read(u5::with(4))? as u8; + Ok(res.try_into().expect("bit extractor failure")) + } + + fn read_5bits(&mut self) -> Result { + let res = self.read(u5::with(5))? as u8; + Ok(res.try_into().expect("bit extractor failure")) + } + + fn read_6bits(&mut self) -> Result { + let res = self.read(u5::with(6))? as u8; + Ok(res.try_into().expect("bit extractor failure")) + } + + fn read_7bits(&mut self) -> Result { + let res = self.read(u5::with(7))? as u8; + Ok(res.try_into().expect("bit extractor failure")) + } + + fn read_byte(&mut self) -> Result { + let res = self.read(u5::with(8))? as u8; + Ok(res) + } + + fn read_word(&mut self) -> Result { + let res = self.read(u5::with(16))? as u16; + Ok(res) + } + + fn read_fixed( + &mut self, + f: impl FnOnce([u8; LEN]) -> N, + ) -> Result { + let pos = self.read_word()? as usize; + let end = pos + LEN; + if end > self.data.as_ref().len() { + return Err(CodeEofError); + } + let mut buf = [0u8; LEN]; + buf.copy_from_slice(&self.data.as_ref()[pos..end]); + Ok(f(buf)) + } + + fn read_bytes(&mut self) -> Result<(SmallBlob, bool), CodeEofError> { + let pos = self.read_word()? as usize; + let end = pos + self.read_word()? as usize; + let ck = end >= self.data.as_ref().len(); + let data = &self.data.as_ref()[pos.min(0xFF)..end.min(0xFF)]; + Ok((SmallBlob::from_slice_checked(data), ck)) + } + + fn read_ref(&mut self) -> Result + where LibId: Sized { + let pos = self.read_byte()? as usize; + Ok(self.libs.iter().nth(pos).copied().unwrap_or_default()) + } + + fn check_aligned(&self) { + debug_assert_eq!(self.bit_pos, u3::ZERO, "not all instruction operands are read") + } +} + +impl<'a, C, D> BytecodeWrite for Marshaller<'a, C, D> +where + C: AsRef<[u8]> + AsMut<[u8]> + Extend, + D: AsRef<[u8]> + AsMut<[u8]> + Extend, + Self: 'a, +{ + type Error = MarshallError; + + fn write_1bit(&mut self, data: impl Into) -> Result<(), MarshallError> { + self.write(data.into().into_u8() as u32, u5::with(1)) + .map_err(MarshallError::from) + } + + fn write_2bits(&mut self, data: impl Into) -> Result<(), MarshallError> { + self.write(data.into().to_u8() as u32, u5::with(2)) + .map_err(MarshallError::from) + } + + fn write_3bits(&mut self, data: impl Into) -> Result<(), MarshallError> { + self.write(data.into().to_u8() as u32, u5::with(3)) + .map_err(MarshallError::from) + } + + fn write_4bits(&mut self, data: impl Into) -> Result<(), MarshallError> { + self.write(data.into().to_u8() as u32, u5::with(4)) + .map_err(MarshallError::from) + } + + fn write_5bits(&mut self, data: impl Into) -> Result<(), MarshallError> { + self.write(data.into().to_u8() as u32, u5::with(5)) + .map_err(MarshallError::from) + } + + fn write_6bits(&mut self, data: impl Into) -> Result<(), MarshallError> { + self.write(data.into().to_u8() as u32, u5::with(6)) + .map_err(MarshallError::from) + } + + fn write_7bits(&mut self, data: impl Into) -> Result<(), MarshallError> { + self.write(data.into().to_u8() as u32, u5::with(7)) + .map_err(MarshallError::from) + } + + fn write_byte(&mut self, data: u8) -> Result<(), MarshallError> { + self.write(data as u32, u5::with(8)) + .map_err(MarshallError::from) + } + + fn write_word(&mut self, data: u16) -> Result<(), MarshallError> { + self.write(data as u32, u5::with(16)) + .map_err(MarshallError::from) + } + + fn write_fixed(&mut self, data: [u8; LEN]) -> Result<(), Self::Error> { + if LEN >= u16::MAX as usize { + return Err(MarshallError::DataExceedsLimit(LEN)); + } + let offset = self.write_unique(&data)?; + self.write_word(offset) + } + + fn write_bytes(&mut self, data: &[u8]) -> Result<(), Self::Error> { + let len = data.len(); + if len >= u16::MAX as usize { + return Err(MarshallError::DataExceedsLimit(len)); + } + let offset = self.write_unique(data)?; + self.write_word(offset)?; + self.write_word(len as u16) + } + + fn write_ref(&mut self, id: LibId) -> Result<(), Self::Error> { + let pos = self + .libs + .iter() + .position(|lib| *lib == id) + .ok_or(MarshallError::LibAbsent(id))?; + self.write_byte(pos as u8) + } + + fn check_aligned(&self) { + debug_assert_eq!(self.bit_pos, u3::ZERO, "not all instruction operands are written") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn read() { + let libseg = LibsSeg::default(); + let mut marshaller = Marshaller::with([0b01010111, 0b00001001], [], &libseg); + assert_eq!(marshaller.read_2bits().unwrap().to_u8(), 0b00000011); + assert_eq!(marshaller.read_2bits().unwrap().to_u8(), 0b00000001); + assert_eq!(marshaller.read_byte().unwrap(), 0b10010101); + + let mut marshaller = Marshaller::with([0b01010111, 0b00001001], [], &libseg); + assert_eq!(marshaller.read_2bits().unwrap().to_u8(), 0b00000011); + assert_eq!(marshaller.read_3bits().unwrap().to_u8(), 0b00000101); + assert_eq!(marshaller.read_byte().unwrap(), 0b01001010); + + let mut marshaller = Marshaller::with([0b01110111, 0b00001111], [], &libseg); + assert_eq!(marshaller.read_byte().unwrap(), 0b01110111); + assert_eq!(marshaller.read_3bits().unwrap().to_u8(), 0b00000111); + assert_eq!(marshaller.read_5bits().unwrap().to_u8(), 0b00000001); + + let bytes = 0b11101011_11110000_01110111; + let mut marshaller = Marshaller::with(u32::to_le_bytes(bytes), [], &libseg); + assert_eq!(marshaller.read(u5::with(24)).unwrap(), bytes); + } + + #[test] + fn read_eof() { + let libseg = LibsSeg::default(); + let mut marshaller = Marshaller::with([0b01010111], [], &libseg); + assert_eq!(marshaller.read_2bits().unwrap().to_u8(), 0b00000011); + assert_eq!(marshaller.read_2bits().unwrap().to_u8(), 0b00000001); + assert!(marshaller.read_byte().is_err()); + } + + #[test] + fn write() { + let libseg = LibsSeg::default(); + let mut marshaller = Marshaller::with(vec![], vec![], &libseg); + marshaller.write_2bits(u2::with(0b00000011)).unwrap(); + marshaller.write_3bits(u3::with(0b00000101)).unwrap(); + marshaller.write_7bits(u7::with(0b01011111)).unwrap(); + marshaller.write_byte(0b11100111).unwrap(); + marshaller.write_bool(true).unwrap(); + marshaller.write_3bits(u3::with(0b00000110)).unwrap(); + let two_bytes = 0b11110000_10101010u16; + marshaller.write_word(two_bytes).unwrap(); + let number = 255u8; + marshaller.write_fixed(255u8.to_le_bytes()).unwrap(); + let (code, data) = marshaller.finish(); + + let mut marshaller = Marshaller::with(code, data, &libseg); + assert_eq!(marshaller.read_2bits().unwrap().to_u8(), 0b00000011); + assert_eq!(marshaller.read_3bits().unwrap().to_u8(), 0b00000101); + assert_eq!(marshaller.read_7bits().unwrap().to_u8(), 0b01011111); + assert_eq!(marshaller.read_byte().unwrap(), 0b11100111); + assert!(marshaller.read_bool().unwrap()); + assert_eq!(marshaller.read_3bits().unwrap().to_u8(), 0b00000110); + assert_eq!(marshaller.read_word().unwrap(), two_bytes); + assert_eq!(marshaller.read_fixed(u8::from_le_bytes).unwrap(), number); + } + + #[test] + fn write_data() { + let libseg = LibsSeg::default(); + let mut marshaller = Marshaller::with(vec![], vec![], &libseg); + marshaller.write_fixed(256u16.to_le_bytes()).unwrap(); + assert_eq!(marshaller.data, vec![0, 1]); + } + + #[test] + fn write_eof() { + let libseg = LibsSeg::default(); + let mut marshaller = Marshaller::with(vec![0x00; 0xFFFD], vec![], &libseg); + marshaller.seek(0xFFFD).unwrap_err(); + marshaller.byte_pos = 0xFFFD; + marshaller.write_2bits(u2::with(0b00000011)).unwrap(); + marshaller.write_3bits(u3::with(0b00000101)).unwrap(); + marshaller.write_7bits(u7::with(0b01011111)).unwrap(); + marshaller.write_byte(0b11100111).unwrap_err(); + assert_eq!(&marshaller.bytecode[0xFFFD..], &[0b11110111, 0b1011]); + } +} diff --git a/src/library/mod.rs b/src/library/mod.rs index 99ea1ba..0b3ab01 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -3,37 +3,31 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. -//! Business logic and data structures for working with AluVM code libraries - -pub mod constants; -mod cursor; mod lib; -mod rw; -mod segs; +#[cfg(feature = "armor")] +pub mod armor; +mod assembler; +mod marshaller; +mod exec; -pub use cursor::Cursor; -#[cfg(feature = "ascii-armor")] -pub use lib::LibArmorError; -pub use lib::{AssemblerError, Lib, LibId, LibSite}; -pub use rw::{CodeEofError, Read, Write, WriteError}; -pub use segs::{IsaName, IsaSeg, IsaSegError, LibSeg, SegmentError}; +pub use lib::{Lib, LibId, LibSite, LibsSeg}; +pub use marshaller::{MarshallError, Marshaller}; diff --git a/src/library/rw.rs b/src/library/rw.rs deleted file mode 100644 index 025dacc..0000000 --- a/src/library/rw.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -use amplify::num::{u1, u2, u24, u3, u4, u5, u6, u7}; - -use super::LibId; -use crate::data::Number; -use crate::isa::{Instr, InstructionSet}; -use crate::reg::NumericRegister; - -// I had an idea of putting Read/Write functionality into `amplify` crate, -// but it is quire specific to the fact that it uses `u16`-sized underlying -// bytestring, which is specific to client-side-validation and this VM and not -// generic enough to become part of the `amplify` library - -/// Error indicating that an end of code segment boundary is reached during read or write operation -#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[display("attempt to read or write outside of code segment (i.e. at position > 2^16)")] -#[cfg_attr(feature = "std", derive(Error))] -pub struct CodeEofError; - -/// Errors write operations -#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] -#[cfg_attr(feature = "std", derive(Error))] -#[display(doc_comments)] -pub enum WriteError { - /// attempt to read or write outside of code segment (i.e. at position > 2^16) - #[from(CodeEofError)] - CodeNotFittingSegment, - - /// data size {0} exceeds limit of 2^16 bytes - DataExceedsLimit(usize), - - /// attempt to write data which does not fit code segment - DataNotFittingSegment, - - /// attempt to write library reference for the lib id {0} which is not a part of program - /// segment - LibAbsent(LibId), -} - -mod private { - use super::super::Cursor; - - pub trait Sealed {} - - impl<'a, T, D> Sealed for Cursor<'a, T, D> - where - T: AsRef<[u8]>, - D: AsRef<[u8]>, - Self: 'a, - { - } -} - -/// Trait for reading instruction data from bytecode -pub trait Read: private::Sealed { - /// Returns current byte offset of the cursor. Does not accounts bits. - /// If the position is exactly at EOF, returns `None`. - fn pos(&self) -> u16; - /// Sets current cursor byte offset to the provided value, if it is less than the underlying - /// buffer length - /// - /// # Returns - /// - /// Previous position - fn seek(&mut self, byte_pos: u16) -> Result; - /// Returns whether end of the bytecode is reached - fn is_eof(&self) -> bool; - /// Peeks a single byte without moving cursor - fn peek_u8(&self) -> Result; - /// Reads single bit as a bool values - fn read_bool(&mut self) -> Result; - /// Reads single bit - fn read_u1(&mut self) -> Result; - /// Reads two bits - fn read_u2(&mut self) -> Result; - /// Reads three bits - fn read_u3(&mut self) -> Result; - /// Reads four bits - fn read_u4(&mut self) -> Result; - /// Reads five bits - fn read_u5(&mut self) -> Result; - /// Reads six bits - fn read_u6(&mut self) -> Result; - /// Reads seven bits - fn read_u7(&mut self) -> Result; - /// Reads full byte - fn read_u8(&mut self) -> Result; - /// Reads two bytes and converts them into a signed integer - fn read_i8(&mut self) -> Result; - /// Reads two bytes - fn read_u16(&mut self) -> Result; - /// Reads two bytes and converts them into a signed integer - fn read_i16(&mut self) -> Result; - /// Reads three bytes - fn read_u24(&mut self) -> Result; - /// Reads library id - fn read_lib(&mut self) -> Result; - /// Reads bytestring from data segment - fn read_data(&mut self) -> Result<(&[u8], bool), CodeEofError>; - /// Reads number representation from a data segment - fn read_number(&mut self, reg: impl NumericRegister) -> Result; -} - -/// Trait for writing instruction data into bytecode -pub trait Write: private::Sealed { - /// Writes a single bit from a bool value - fn write_bool(&mut self, data: bool) -> Result<(), WriteError>; - /// Writes a single bit - fn write_u1(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes two bits - fn write_u2(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes three bits - fn write_u3(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes four bits - fn write_u4(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes five bits - fn write_u5(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes six bits - fn write_u6(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes seven bits - fn write_u7(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes full byte - fn write_u8(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes full byte corresponding to signed integer representation - fn write_i8(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes two bytes - fn write_u16(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes two bytes corresponding to signed integer representation - fn write_i16(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes three bytes - fn write_u24(&mut self, data: impl Into) -> Result<(), WriteError>; - /// Writes library id into data segment - fn write_lib(&mut self, data: LibId) -> Result<(), WriteError>; - /// Writes bytestring into data segment - fn write_data(&mut self, bytes: impl AsRef<[u8]>) -> Result<(), WriteError>; - /// Writes number representation into data segment - fn write_number(&mut self, reg: impl NumericRegister, value: Number) -> Result<(), WriteError>; - /// In-place instruction editing - fn edit(&mut self, pos: u16, editor: F) -> Result<(), E> - where - F: FnOnce(&mut Instr) -> Result<(), E>, - E: From, - S: InstructionSet; -} diff --git a/src/library/segs.rs b/src/library/segs.rs deleted file mode 100644 index 17c07fc..0000000 --- a/src/library/segs.rs +++ /dev/null @@ -1,260 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -//! Data structures representing static library segments - -#[cfg(feature = "alloc")] -use alloc::borrow::ToOwned; -use alloc::collections::{btree_set, BTreeSet}; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::fmt::{self, Debug, Display, Formatter}; -use core::str::FromStr; - -use amplify::confinement; -use amplify::confinement::Confined; -use strict_encoding::stl::{AlphaCaps, AlphaCapsNum}; -use strict_encoding::{InvalidRString, RString}; - -use crate::library::constants::{ - ISAE_SEGMENT_MAX_COUNT, ISA_ID_MAX_LEN, ISA_ID_MIN_LEN, LIBS_SEGMENT_MAX_COUNT, -}; -use crate::library::LibId; -use crate::LIB_NAME_ALUVM; - -/// Errors while processing binary-encoded segment data -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, From)] -#[cfg_attr(feature = "std", derive(Error))] -#[display(doc_comments)] -pub enum SegmentError { - /// the size of the CODE segment is {0}, which exceeds `CODE_SEGMENT_MAX_LEN` - CodeSegmentTooLarge(usize), - - /// the size of the DATA segment is {0}, which exceeds `DATA_SEGMENT_MAX_LEN` - DataSegmentTooLarge(usize), - - /// ISA segment error - #[display(inner)] - #[from] - IsaeSegment(IsaSegError), -} - -/// Errors while processing ISA extensions segment -#[derive(Clone, PartialEq, Eq, Hash, Debug, Display, From)] -#[cfg_attr(feature = "std", derive(Error))] -#[display(doc_comments)] -pub enum IsaSegError { - /// ISA segment is invalid, specifically {0} - #[from] - Number(confinement::Error), - - /// ISA id {0} has a wrong name, specifically {1} - Name(String, InvalidRString), -} - -/// ISA extension name. -#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)] -#[wrapper(Deref, Display, FromStr)] -#[derive(StrictDumb, StrictType, StrictDecode)] -#[cfg_attr(feature = "std", derive(StrictEncode))] -#[strict_type(lib = LIB_NAME_ALUVM)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct IsaName(RString); - -impl_ident_type!(IsaName); -impl_ident_subtype!(IsaName); - -/// ISA extensions segment. -#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictDecode)] -#[cfg_attr(feature = "std", derive(StrictEncode))] -#[strict_type(lib = LIB_NAME_ALUVM)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct IsaSeg(Confined, 0, ISAE_SEGMENT_MAX_COUNT>); - -impl IsaSeg { - /// Constructs ISAE segment from a string. - /// - /// ISAE segment deterministically orders ISAE ids lexicographically. This is not a requirement, - /// but just a good practice for producing the same code on different platforms. - /// - /// # Error - /// - /// Errors with [`IsaSegError`] if the segment can't be correctly constructed from the probided - /// data. - #[inline] - pub fn with(s: &'static str) -> Self { - Self::from_str(s).expect("invalid hardcoded ISA extension name") - } - - /// Returns number of ISA extensions in the ISAE segment - #[inline] - pub fn count(&self) -> u8 { self.0.len() as u8 } - - /// Returns specific ISA id with a given index in the segment. - #[inline] - pub fn at(&self, index: u8) -> Option { - self.0.iter().enumerate().nth(index as usize).map(|(_, isa)| isa).cloned() - } - - /// Constructs ISA segment from an iterator over ISA extension names. - #[inline] - pub fn try_from_iter( - iter: impl IntoIterator, - ) -> Result { - Confined::try_from_iter(iter).map(Self) - } -} - -impl IntoIterator for IsaSeg { - type Item = IsaName; - type IntoIter = btree_set::IntoIter; - - fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } -} - -impl Display for IsaSeg { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.0.iter().map(IsaName::to_string).collect::>().join(" ")) - } -} - -impl FromStr for IsaSeg { - type Err = IsaSegError; - - fn from_str(s: &str) -> Result { - let mut seg = Confined::, 0, ISAE_SEGMENT_MAX_COUNT>::new(); - for isa in s.split(' ') { - let name = - IsaName::from_str(isa).map_err(|err| IsaSegError::Name(isa.to_owned(), err))?; - seg.push(name)?; - } - Ok(Self(seg)) - } -} - -/// Library segment data keeping collection of libraries which MAY be used in some program. -/// Libraries are referenced in the bytecode using 16-bit position number in this index. -/// -/// Library segment keeps ordered collection of [`LibId`] such that the code calling library methods -/// does not need to reference the whole 32-byte id each time and can just provide the library index -/// in the program segment (1 byte). Thus, the total number of libraries which can be used by a -/// program is limited to 2^8, and the maximum size of program segment to 32*2^8 (8 kB). -/// -/// Runtime implementations MUST ensure that the total number of libraries used by a single program -/// do not exceeds [`LIBS_MAX_TOTAL`], limiting the maximum possible total program size for -/// AluVM to ~65 MB. -/// -/// NB: The program can reference position outside the scope of the library segment size; in this -/// case VM performs no-operation and sets `st0` to false. -/// -/// Libraries MUST be referenced in the program segment in lexicographic order. -/// -/// The implementation MUST ensure that the size of the index never exceeds `u16::MAX`. -/// -/// [`LIBS_MAX_TOTAL`]: super::constants::LIBS_MAX_TOTAL -#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictDecode)] -#[cfg_attr(feature = "std", derive(StrictEncode))] -#[strict_type(lib = LIB_NAME_ALUVM)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct LibSeg(Confined, 0, LIBS_SEGMENT_MAX_COUNT>); - -impl LibSeg { - /// Returns iterator over unique libraries iterated in the deterministic (lexicographic) order - #[inline] - pub fn iter(&self) -> ::alloc::collections::btree_set::Iter { self.into_iter() } -} - -impl<'a> IntoIterator for &'a LibSeg { - type Item = &'a LibId; - type IntoIter = ::alloc::collections::btree_set::Iter<'a, LibId>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { self.0.iter() } -} - -impl LibSeg { - /// Returns number of libraries in the lib segment - #[inline] - pub fn count(&self) -> u8 { self.0.len() as u8 } - - /// Returns library id with a given index - #[inline] - pub fn at(&self, index: u8) -> Option { self.0.iter().nth(index as usize).copied() } - - /// Returns index of a library. - /// - /// The program can reference position outside the scope of the library segment size; in this - /// case VM performs no-operation and sets `st0` to false. - /// - /// # Returns - /// - /// If the library is not present in program segment, returns `None`. - #[inline] - pub fn index(&self, lib: LibId) -> Option { - self.0.iter().position(|l| *l == lib).map(|i| i as u8) - } - - /// Constructs libraries segment from an iterator over library ids. - #[inline] - pub fn try_from_iter( - iter: impl IntoIterator, - ) -> Result { - Confined::try_from_iter(iter).map(Self) - } -} - -impl Display for LibSeg { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.0.iter().enumerate().try_for_each(|(line, lib)| { - writeln!( - f, - "{:>2$}{}", - "", - lib, - if line == 0 { 0 } else { f.width().unwrap_or_default() } - ) - }) - } -} diff --git a/src/reg/core_regs.rs b/src/reg/core_regs.rs deleted file mode 100644 index dd52e68..0000000 --- a/src/reg/core_regs.rs +++ /dev/null @@ -1,912 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -use alloc::string::ToString; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::fmt::{self, Debug, Formatter}; - -use amplify::hex::ToHex; -use amplify::num::apfloat::{ieee, Float}; -use amplify::num::{u1024, u256, u512}; -use half::bf16; - -use super::{Reg, Reg32, RegA, RegAFR, RegF, RegR, RegS}; -use crate::data::{ByteStr, MaybeNumber, Number, RegValue}; -use crate::isa::InstructionSet; -use crate::library::LibSite; - -/// Maximal size of call stack. -/// -/// Equals to 2^16 (limited by `cy0` and `cp0` bit size) -pub const CALL_STACK_SIZE: usize = 1 << 16; - -/// Structure keeping state of all registers in a single microprosessor/VM core -#[derive(Clone)] -pub struct CoreRegs { - // Arithmetic integer registers: - pub(crate) a8: [Option; 32], - pub(crate) a16: [Option; 32], - pub(crate) a32: [Option; 32], - pub(crate) a64: [Option; 32], - pub(crate) a128: [Option; 32], - pub(crate) a256: [Option; 32], - pub(crate) a512: [Option; 32], - pub(crate) a1024: Box<[Option; 32]>, - - // Arithmetic float registers - pub(crate) f16b: [Option; 32], - pub(crate) f16: [Option; 32], - pub(crate) f32: [Option; 32], - pub(crate) f64: [Option; 32], - pub(crate) f80: [Option; 32], - pub(crate) f128: [Option; 32], - pub(crate) f256: [Option; 32], - // TODO(#5) Implement tapered floating point type - pub(crate) f512: [Option; 32], - - // Non-arithmetic registers: - pub(crate) r128: [Option<[u8; 16]>; 32], - pub(crate) r160: [Option<[u8; 20]>; 32], - pub(crate) r256: [Option<[u8; 32]>; 32], - pub(crate) r512: [Option<[u8; 64]>; 32], - pub(crate) r1024: Box<[Option<[u8; 128]>; 32]>, - pub(crate) r2048: Box<[Option<[u8; 256]>; 32]>, - pub(crate) r4096: Box<[Option<[u8; 512]>; 32]>, - pub(crate) r8192: Box<[Option<[u8; 1024]>; 32]>, - - /// String and bytestring registers - pub(crate) s16: Box<[Option; 16]>, - - /// Control flow register which stores result of equality, comparison, boolean check and - /// overflowing operations. Initialized with `true`. - pub(crate) st0: bool, - - /// Counts number of jumps (possible cycles). The number of jumps is limited by 2^16 per - /// script. - cy0: u16, - - /// Complexity accumulator / counter. - /// - /// Each instruction has associated computational complexity level. This register sums - /// complexity of executed instructions. - /// - /// # See also - /// - /// - [`CoreRegs::cy0`] register - /// - [`CoreRegs::cl0`] register - ca0: u64, - - /// Complexity limit - /// - /// If this register has a value set, once [`CoreRegs::ca0`] will reach this value the VM will - /// stop program execution setting `st0` to `false`. - cl0: Option, - - /// Call stack - /// - /// # See also - /// - /// - [`CALL_STACK_SIZE`] constant - /// - [`CoreRegs::cp0`] register - cs0: Vec, - - /// Defines "top" of the call stack - cp0: u16, -} - -impl Default for CoreRegs { - #[inline] - fn default() -> Self { - CoreRegs { - a8: Default::default(), - a16: Default::default(), - a32: Default::default(), - a64: Default::default(), - a128: Default::default(), - a256: Default::default(), - a512: Default::default(), - a1024: Default::default(), - - f16b: Default::default(), - f16: Default::default(), - f32: Default::default(), - f64: Default::default(), - f80: Default::default(), - f128: Default::default(), - f256: Default::default(), - f512: Default::default(), - - r128: Default::default(), - r160: Default::default(), - r256: Default::default(), - r512: Default::default(), - r1024: Default::default(), - r2048: Default::default(), - r4096: Default::default(), - r8192: Default::default(), - - s16: Default::default(), - - st0: true, - cy0: 0, - ca0: 0, - cl0: None, - cs0: vec![LibSite::default(); CALL_STACK_SIZE], - cp0: 0, - } - } -} - -impl CoreRegs { - /// Initializes register state. Sets `st0` to `true`, counters to zero, call stack to empty and - /// the rest of registers to `None` value. - /// - /// Performs exactly the same as [`CoreRegs::default()`]. - #[inline] - pub fn new() -> CoreRegs { CoreRegs::default() } - - pub(crate) fn jmp(&mut self) -> Result<(), ()> { - self.cy0 - .checked_add(1) - .map(|cy| self.cy0 = cy) - .ok_or_else(|| { - self.st0 = false; - }) - .map(|_| ()) - } - - pub(crate) fn call(&mut self, site: LibSite) -> Result<(), ()> { - self.cy0 - .checked_add(1) - .map(|cy| self.cy0 = cy) - .ok_or_else(|| { - self.st0 = false; - }) - .map(|_| { - self.cs0[self.cp0 as usize] = site; - }) - .and_then(|_| { - self.cp0 - .checked_add(1) - .ok_or_else(|| { - self.st0 = false; - }) - .map(|_| ()) - }) - } - - pub(crate) fn ret(&mut self) -> Option { - if self.cp0 == 0 { - None - } else { - self.cs0[self.cp0 as usize] = LibSite::default(); - self.cp0 -= 1; - Some(self.cs0[self.cp0 as usize]) - } - } - - /// Get value from `a8` register. - pub fn a8(&self, idx: impl Into) -> Option { self.a8[idx.into().to_usize()] } - /// Get value from `a16` register. - pub fn a16(&self, idx: impl Into) -> Option { self.a16[idx.into().to_usize()] } - /// Get value from `a32` register. - pub fn a32(&self, idx: impl Into) -> Option { self.a32[idx.into().to_usize()] } - /// Get value from `a64` register. - pub fn a64(&self, idx: impl Into) -> Option { self.a64[idx.into().to_usize()] } - /// Get value from `a128` register. - pub fn a128(&self, idx: impl Into) -> Option { self.a128[idx.into().to_usize()] } - - /// Sets `a8` register to a given value. Returns previous register value. - pub fn set_a8(&mut self, idx: impl Into, val: u8) -> Option { - self.a8[idx.into().to_usize()].replace(val) - } - /// Sets `a16` register to a given value. Returns previous register value. - pub fn set_a16(&mut self, idx: impl Into, val: u16) -> Option { - self.a16[idx.into().to_usize()].replace(val) - } - /// Sets `a32` register to a given value. Returns previous register value. - pub fn set_a32(&mut self, idx: impl Into, val: u32) -> Option { - self.a32[idx.into().to_usize()].replace(val) - } - /// Sets `a64` register to a given value. Returns previous register value. - pub fn set_a64(&mut self, idx: impl Into, val: u64) -> Option { - self.a64[idx.into().to_usize()].replace(val) - } - /// Sets `a128` register to a given value. Returns previous register value. - pub fn set_a128(&mut self, idx: impl Into, val: u128) -> Option { - self.a128[idx.into().to_usize()].replace(val) - } - - /// Clears `a8` register (sets its value to `None`). Returns previous register value. - pub fn clr_a8(&mut self, idx: impl Into) -> Option { - self.a8[idx.into().to_usize()].take() - } - /// Clears `a16` register (sets its value to `None`). Returns previous register value. - pub fn clr_a16(&mut self, idx: impl Into) -> Option { - self.a16[idx.into().to_usize()].take() - } - /// Clears `a32` register (sets its value to `None`). Returns previous register value. - pub fn clr_a32(&mut self, idx: impl Into) -> Option { - self.a32[idx.into().to_usize()].take() - } - /// Clears `a64` register (sets its value to `None`). Returns previous register value. - pub fn clr_a64(&mut self, idx: impl Into) -> Option { - self.a64[idx.into().to_usize()].take() - } - /// Clears `a128` register (sets its value to `None`). Returns previous register value. - pub fn clr_a128(&mut self, idx: impl Into) -> Option { - self.a128[idx.into().to_usize()].take() - } - - /// Gets `s16` register value. - pub fn s16(&self, idx: impl Into) -> Option<&ByteStr> { - self.s16[idx.into().as_usize()].as_ref() - } - - /// Sets `s16` register to a given value. Returns previous register value. - pub fn set_s16(&mut self, idx: impl Into, val: impl Into) -> Option { - self.s16[idx.into().as_usize()].replace(val.into()) - } - - /// Clears `s16` register (sets to `None`). Returns previous register value. - pub fn clr_s16(&mut self, idx: impl Into) -> Option { - self.s16[idx.into().as_usize()].take() - } - - /// 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.s16(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) => { - let n = match a { - RegA::A8 => self.a8[index].map(Number::from), - RegA::A16 => self.a16[index].map(Number::from), - RegA::A32 => self.a32[index].map(Number::from), - RegA::A64 => self.a64[index].map(Number::from), - RegA::A128 => self.a128[index].map(Number::from), - RegA::A256 => self.a256[index].map(Number::from), - RegA::A512 => self.a512[index].map(Number::from), - RegA::A1024 => self.a1024[index].map(Number::from), - }; - n.into() - } - - RegAFR::R(r) => { - let n = match r { - RegR::R128 => self.r128[index].map(Number::from), - RegR::R160 => self.r160[index].map(Number::from), - RegR::R256 => self.r256[index].map(Number::from), - RegR::R512 => self.r512[index].map(Number::from), - RegR::R1024 => self.r1024[index].map(Number::from), - RegR::R2048 => self.r2048[index].map(Number::from), - RegR::R4096 => self.r4096[index].map(Number::from), - RegR::R8192 => self.r8192[index].map(Number::from), - }; - n.into() - } - - RegAFR::F(f) => { - let n = match f { - RegF::F16B => self.f16b[index].map(MaybeNumber::from), - RegF::F16 => self.f16[index].map(MaybeNumber::from), - RegF::F32 => self.f32[index].map(MaybeNumber::from), - RegF::F64 => self.f64[index].map(MaybeNumber::from), - RegF::F80 => self.f80[index].map(MaybeNumber::from), - RegF::F128 => self.f128[index].map(MaybeNumber::from), - RegF::F256 => self.f256[index].map(MaybeNumber::from), - RegF::F512 => self.f512[index].map(MaybeNumber::from), - }; - n.unwrap_or_else(MaybeNumber::none) - } - } - } - - /// Retrieves mutable reference to R-register value - pub fn get_r_mut( - &mut self, - reg: impl Into, - index: impl Into, - ) -> Option<&mut [u8]> { - let index = index.into().to_usize(); - match reg.into() { - RegR::R128 => self.r128[index].as_mut().map(|x| x.as_mut_slice()), - RegR::R160 => self.r160[index].as_mut().map(|x| x.as_mut_slice()), - RegR::R256 => self.r256[index].as_mut().map(|x| x.as_mut_slice()), - RegR::R512 => self.r512[index].as_mut().map(|x| x.as_mut_slice()), - RegR::R1024 => self.r1024[index].as_mut().map(|x| x.as_mut_slice()), - RegR::R2048 => self.r2048[index].as_mut().map(|x| x.as_mut_slice()), - RegR::R4096 => self.r4096[index].as_mut().map(|x| x.as_mut_slice()), - RegR::R8192 => self.r8192[index].as_mut().map(|x| x.as_mut_slice()), - } - } - - /// Returns value from one of `S`-registers - #[inline] - #[deprecated(since = "0.11.0-beta.9", note = "use `s16` method")] - pub fn get_s(&self, index: impl Into) -> Option<&ByteStr> { - self.s16[index.into().as_usize()].as_ref() - } - - /// Returns value from two registers only if both of them contain a value; otherwise returns - /// `None`. - #[inline] - pub fn get_n2( - &self, - reg1: impl Into, - idx1: impl Into, - reg2: impl Into, - idx2: impl Into, - ) -> Option<(Number, Number)> { - 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_s2( - &self, - idx1: impl Into, - idx2: impl Into, - ) -> Option<(&ByteStr, &ByteStr)> { - self.s16(idx1).and_then(|val1| self.s16(idx2).map(|val2| (val1, val2))) - } - - /// Assigns the provided value to the register bit-wise. Silently discards most significant bits - /// until the value fits register bit size. - /// - /// Returns `true` if the value was not `None` - pub fn set_n( - &mut self, - reg: impl Into, - index: impl Into, - value: impl Into, - ) -> bool { - let index = index.into() as usize; - let value: Option = value.into().into(); - match reg.into() { - RegAFR::A(a) => match a { - RegA::A8 => self.a8[index] = value.map(Number::into), - RegA::A16 => self.a16[index] = value.map(Number::into), - RegA::A32 => self.a32[index] = value.map(Number::into), - RegA::A64 => self.a64[index] = value.map(Number::into), - RegA::A128 => self.a128[index] = value.map(Number::into), - RegA::A256 => self.a256[index] = value.map(Number::into), - RegA::A512 => self.a512[index] = value.map(Number::into), - RegA::A1024 => self.a1024[index] = value.map(Number::into), - }, - RegAFR::R(r) => match r { - RegR::R128 => self.r128[index] = value.map(Number::into), - RegR::R160 => self.r160[index] = value.map(Number::into), - RegR::R256 => self.r256[index] = value.map(Number::into), - RegR::R512 => self.r512[index] = value.map(Number::into), - RegR::R1024 => self.r1024[index] = value.map(Number::into), - RegR::R2048 => self.r2048[index] = value.map(Number::into), - RegR::R4096 => self.r4096[index] = value.map(Number::into), - RegR::R8192 => self.r8192[index] = value.map(Number::into), - }, - RegAFR::F(f) => match f { - RegF::F16B => self.f16b[index] = value.map(Number::into), - RegF::F16 => self.f16[index] = value.map(Number::into), - RegF::F32 => self.f32[index] = value.map(Number::into), - RegF::F64 => self.f64[index] = value.map(Number::into), - RegF::F80 => self.f80[index] = value.map(Number::into), - RegF::F128 => self.f128[index] = value.map(Number::into), - RegF::F256 => self.f256[index] = value.map(Number::into), - RegF::F512 => self.f512[index] = value.map(Number::into), - }, - } - value.is_some() - } - - /// Assigns the provided value to the register bit-wise if the register is not initialized. - /// Silently discards most significant bits until the value fits register bit size. - /// - /// Returns `false` if the register is initialized and the value is not `None`. - #[inline] - pub fn set_n_if( - &mut self, - reg: impl Into, - index: impl Into, - value: impl Into, - ) -> bool { - let reg = reg.into(); - let index = index.into(); - if self.get_n(reg, index).is_none() { - self.set_n(reg, index, value) - } else { - value.into().is_none() - } - } - - /// Assigns the provided value to the string register. - /// - /// Returns `true` if the value was not `None`. - #[deprecated(since = "0.11.0-beta.9", note = "use `set_s16` method")] - pub fn set_s(&mut self, index: impl Into, value: Option>) -> bool { - let reg = &mut self.s16[index.into().as_usize()]; - let was_set = reg.is_some(); - *reg = value.map(|v| v.into()); - was_set - } - - /// Assigns the provided value to the string register if the register is not initialized. - /// - /// Returns `false` if the register is initialized and the value is not `None`. - #[deprecated(since = "0.11.0-beta.9")] - pub fn set_s_if(&mut self, index: impl Into, value: Option>) -> bool { - let index = index.into(); - if self.s16(index).is_none() { - #[allow(deprecated)] - self.set_s(index, value) - } else { - value.is_none() - } - } - - /// Executes provided operation (as callback function) if and only if all the provided registers - /// contain a value (initialized). Otherwise, sets destination to `None` and does not call the - /// callback. - #[inline] - #[allow(clippy::too_many_arguments)] - pub fn op( - &mut self, - reg1: impl Into, - src1: impl Into, - reg2: impl Into, - src2: impl Into, - reg3: impl Into, - dst: impl Into, - op: fn(Number, Number) -> Number, - ) { - 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_n(reg3.into(), dst, reg_val); - } - - /// Accumulates complexity of the instruction into `ca0`. - /// - /// Sets `st0` to `false` if the complexity limit is reached or exceeded. Otherwise, does not - /// modify `st0` value. - /// - /// # Returns - /// - /// `false` if `cl0` register has value and the accumulated complexity has reached or exceeded - /// this limit - #[inline] - pub fn acc_complexity(&mut self, instr: impl InstructionSet) -> bool { - self.ca0 = self.ca0.saturating_add(instr.complexity()); - if let Some(limit) = self.cl0 { - if self.ca0 >= limit { - self.st0 = false; - false - } else { - true - } - } else { - true - } - } - - /// Returns vale of `st0` register - #[inline] - pub fn status(&self) -> bool { self.st0 } - - /// Set `st0` value to `false`. - pub fn set_failure(&mut self) -> bool { - let status = self.st0; - self.st0 = false; - status - } -} - -impl Debug for CoreRegs { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let (sect, reg, eq, val, reset) = if f.alternate() { - ("\x1B[0;4;1m", "\x1B[0;1m", "\x1B[0;37;2m", "\x1B[0;32m", "\x1B[0m") - } else { - ("", "", "", "", "") - }; - - write!(f, "{}CTRL:{}\t", sect, reset)?; - write!(f, "{}st0{}={}{} ", reg, eq, val, self.st0)?; - write!(f, "{}cy0{}={}{} ", reg, eq, val, self.cy0)?; - write!(f, "{}ca0{}={}{} ", reg, eq, val, self.ca0)?; - let cl = self.cl0.map(|v| v.to_string()).unwrap_or_else(|| "~".to_string()); - write!(f, "{}cl0{}={}{} ", reg, eq, val, cl)?; - write!(f, "{}cp0{}={}{} ", reg, eq, val, self.cp0)?; - write!(f, "\n\t\t{}cs0{}={}", reg, eq, val)?; - for p in 0..=self.cp0 { - write!(f, "{}\n\t\t ", self.cs0[p as usize])?; - } - - write!(f, "\n{}A-REG:{}\t", sect, reset)?; - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.a8[i] { - write!(f, "{}a8{}[{}{:02}{}]={}{:02X}{}h\t", reg, eq, reset, i, eq, val, v, reset)?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.a16[i] { - write!( - f, - "{}a16{}[{}{:02}{}]={}{:04X}{}h\t", - reg, eq, reset, i, eq, val, v, reset - )?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.a32[i] { - write!( - f, - "{}a32{}[{}{:02}{}]={}{:08X}{}h\t", - reg, eq, reset, i, eq, val, v, reset - )?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.a64[i] { - write!( - f, - "{}a64{}[{}{:02}{}]={}{:016X}{}h\t", - reg, eq, reset, i, eq, val, v, reset - )?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - for i in 0..32 { - if let Some(v) = self.a128[i] { - write!( - f, - "{}a128{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.a256[i] { - let v = Number::from(v); - write!( - f, - "{}a256{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.a512[i] { - let v = Number::from(v); - write!( - f, - "{}a512{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.a1024[i] { - let v = Number::from(v); - write!( - f, - "{}a1024{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - - write!(f, "\n{}F-REG:{}\t", sect, reset)?; - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.f16b[i] { - write!(f, "{}f16b{}[{}{:02}{}]={}{}{}\t", reg, eq, reset, i, eq, val, v, reset)?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.f16[i] { - write!(f, "{}f16{}[{}{:02}{}]={}{}{}\t", reg, eq, reset, i, eq, val, v, reset)?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.f32[i] { - write!(f, "{}f32{}[{}{:02}{}]={}{}{}\t", reg, eq, reset, i, eq, val, v, reset)?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - let mut c = 0; - for i in 0..32 { - if let Some(v) = self.f64[i] { - write!(f, "{}f64{}[{}{:02}{}]={}{}{}\t", reg, eq, reset, i, eq, val, v, reset)?; - c += 1; - } - } - if c > 0 { - f.write_str("\n\t\t")?; - } - for i in 0..32 { - if let Some(v) = self.f80[i] { - write!( - f, - "{}f80{}[{}{:02}{}]={}{}{}\n\t\t", - reg, - eq, - reset, - i, - eq, - val, - v.to_bits(), - reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.f128[i] { - write!( - f, - "{}f128{}[{}{:02}{}]={}{}{}\n\t\t", - reg, - eq, - reset, - i, - eq, - val, - v.to_bits(), - reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.f256[i] { - write!( - f, - "{}f256{}[{}{:02}{}]={}{}{}\n\t\t", - reg, - eq, - reset, - i, - eq, - val, - v.to_bits(), - reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.f512[i] { - let v = Number::from(v); - write!( - f, - "{}f512{}[{}{:02}{}]={}{:x}{}\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - - write!(f, "\n{}R-REG:{}\t", sect, reset)?; - for i in 0..32 { - if let Some(v) = self.r128[i] { - let v = Number::from(v); - write!( - f, - "{}r128{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.r160[i] { - let v = Number::from(v); - write!( - f, - "{}r160{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.r256[i] { - let v = Number::from(v); - write!( - f, - "{}r256{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - for i in 0..32 { - if let Some(v) = self.r512[i] { - let v = Number::from(v); - write!( - f, - "{}r512{}[{}{:02}{}]={}{:X}{}h\n\t\t", - reg, eq, reset, i, eq, val, v, reset - )?; - } - } - for i in 0..32 { - if let Some(v) = &self.r1024[i] { - write!( - f, - "{}r1024{}[{}{:02}{}]={}{}{}h\n\t\t", - reg, - eq, - reset, - i, - eq, - val, - v.to_hex().to_uppercase(), - reset - )?; - } - } - for i in 0..32 { - if let Some(v) = &self.r2048[i] { - write!( - f, - "{}r2048{}[{}{:02}{}]={}{}{}h\n\t\t", - reg, - eq, - reset, - i, - eq, - val, - v.to_hex().to_uppercase(), - reset - )?; - } - } - for i in 0..32 { - if let Some(v) = &self.r4096[i] { - write!( - f, - "{}r4096{}[{}{:02}{}]={}{}{}h\n\t\t", - reg, - eq, - reset, - i, - eq, - val, - v.to_hex().to_uppercase(), - reset - )?; - } - } - for i in 0..32 { - if let Some(v) = &self.r8192[i] { - write!( - f, - "{}r8192{}[{}{:02}{}]={}{}{}h\n\t\t", - reg, - eq, - reset, - i, - eq, - val, - v.to_hex().to_uppercase(), - reset - )?; - } - } - - write!(f, "\n{}S-REG:{}\t", sect, reset)?; - for i in 0..16 { - if let Some(ref v) = self.s16[i] { - write!(f, "{}s16{}[{}{:02}{}]={}{}{}\n\t", reg, eq, reset, i, eq, val, v, reset)?; - } - } - Ok(()) - } -} - -#[cfg(test)] -mod test { - use amplify::num::u4; - - use super::*; - - // Checks that we do not overflow the stack if using all registers - #[test] - fn init_all() { - let mut regs = CoreRegs::new(); - - for reg in RegA::ALL { - for idx in Reg32::ALL { - regs.set_n(reg, idx, u8::from(idx)); - } - } - - for reg in RegF::ALL { - for idx in Reg32::ALL { - regs.set_n(reg, idx, u8::from(idx)); - } - } - - for reg in RegR::ALL { - for idx in Reg32::ALL { - regs.set_n(reg, idx, u8::from(idx)); - } - } - - for idx in 0u8..16 { - regs.set_s16(u4::with(idx), ByteStr::with(format!("string index {idx}"))); - } - - eprintln!("{regs:#?}"); - } -} diff --git a/src/reg/families.rs b/src/reg/families.rs deleted file mode 100644 index 5275f4c..0000000 --- a/src/reg/families.rs +++ /dev/null @@ -1,1172 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -use core::convert::TryFrom; - -use amplify::num::{u1, u3, u4}; - -use crate::data as number; -use crate::reg::Register; - -/// Common set of methods handled by different sets and families of VM registers -pub trait NumericRegister: Register { - /// Register bit dimension - #[inline] - fn bits(&self) -> u16 { self.bytes() * 8 } - - /// Size of the register value in bytes - fn bytes(&self) -> u16; - - /// Returns register layout - fn layout(&self) -> number::Layout; -} - -/// Enumeration of integer arithmetic registers (`A`-registers) -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[repr(u8)] -#[derive(Default)] -pub enum RegA { - /// 8-bit arithmetics register - #[display("a8")] - A8 = 0, - - /// 16-bit arithmetics register - #[display("a16")] - A16 = 1, - - /// 32-bit arithmetics register - #[display("a32")] - A32 = 2, - - /// 64-bit arithmetics register - #[display("a64")] - #[default] - A64 = 3, - - /// 128-bit arithmetics register - #[display("a128")] - A128 = 4, - - /// 256-bit arithmetics register - #[display("a256")] - A256 = 5, - - /// 512-bit arithmetics register - #[display("a512")] - A512 = 6, - - /// 1024-bit arithmetics register - #[display("a1024")] - A1024 = 7, -} - -impl Register for RegA { - #[inline] - fn description() -> &'static str { "A register" } -} - -impl NumericRegister for RegA { - #[inline] - fn bytes(&self) -> u16 { - match self { - RegA::A8 => 1, - RegA::A16 => 2, - RegA::A32 => 4, - RegA::A64 => 8, - RegA::A128 => 16, - RegA::A256 => 32, - RegA::A512 => 64, - RegA::A1024 => 128, - } - } - - #[inline] - fn layout(&self) -> number::Layout { number::Layout::unsigned(self.bytes()) } -} - -impl RegA { - /// Set of all A registers - pub const ALL: [RegA; 8] = [ - RegA::A8, - RegA::A16, - RegA::A32, - RegA::A64, - RegA::A128, - RegA::A256, - RegA::A512, - RegA::A1024, - ]; - - /// Constructs [`RegA`] object for a provided requirement for register bit size - pub fn with(bits: u16) -> Option { - Some(match bits { - 8 => RegA::A8, - 16 => RegA::A16, - 32 => RegA::A32, - 64 => RegA::A64, - 128 => RegA::A128, - 256 => RegA::A256, - 512 => RegA::A512, - 1024 => RegA::A1024, - _ => return None, - }) - } - - /// Returns integer layout [`number::IntLayout`] specific for this register - #[inline] - pub fn int_layout(self) -> number::IntLayout { number::IntLayout::unsigned(self.bytes()) } -} - -impl From<&RegA> for u3 { - fn from(rega: &RegA) -> Self { u3::with(*rega as u8) } -} - -impl From for u3 { - fn from(rega: RegA) -> Self { u3::with(rega as u8) } -} - -impl From for RegA { - fn from(val: u3) -> Self { - match val { - v if v == RegA::A8.into() => RegA::A8, - v if v == RegA::A16.into() => RegA::A16, - v if v == RegA::A32.into() => RegA::A32, - v if v == RegA::A64.into() => RegA::A64, - v if v == RegA::A128.into() => RegA::A128, - v if v == RegA::A256.into() => RegA::A256, - v if v == RegA::A512.into() => RegA::A512, - v if v == RegA::A1024.into() => RegA::A1024, - _ => unreachable!(), - } - } -} - -impl From for RegA { - #[inline] - fn from(reg: RegA2) -> Self { - match reg { - RegA2::A8 => RegA::A8, - RegA2::A16 => RegA::A16, - } - } -} - -impl From<&RegA2> for RegA { - #[inline] - fn from(reg: &RegA2) -> Self { - match reg { - RegA2::A8 => RegA::A8, - RegA2::A16 => RegA::A16, - } - } -} - -impl TryFrom for RegA { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { value.reg_a().ok_or(()) } -} - -/// Enumeration of integer arithmetic registers suited for string addresses (`a8` and `a16` -/// registers) -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[repr(u8)] -#[derive(Default)] -pub enum RegA2 { - /// 8-bit arithmetics register - #[display("a8")] - #[default] - A8 = 0, - - /// 16-bit arithmetics register - #[display("a16")] - A16 = 1, -} - -impl Register for RegA2 { - #[inline] - fn description() -> &'static str { "A8 or A16 register" } -} - -impl NumericRegister for RegA2 { - #[inline] - fn bytes(&self) -> u16 { - match self { - RegA2::A8 => 1, - RegA2::A16 => 2, - } - } - - #[inline] - fn layout(&self) -> number::Layout { number::Layout::unsigned(self.bytes()) } -} - -impl RegA2 { - /// Constructs [`RegA2`] object for a provided requirement for register bit size - pub fn with(bits: u16) -> Option { - Some(match bits { - 8 => RegA2::A8, - 16 => RegA2::A16, - _ => return None, - }) - } -} - -impl From<&RegA2> for u1 { - fn from(rega: &RegA2) -> Self { u1::with(*rega as u8) } -} - -impl From for u1 { - fn from(rega: RegA2) -> Self { u1::with(rega as u8) } -} - -impl From for RegA2 { - fn from(val: u1) -> Self { - match val { - v if v == RegA2::A8.into() => RegA2::A8, - v if v == RegA2::A16.into() => RegA2::A16, - _ => unreachable!(), - } - } -} - -impl TryFrom for RegA2 { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { - match value.reg_a() { - Some(RegA::A8) => Ok(RegA2::A8), - Some(RegA::A16) => Ok(RegA2::A16), - _ => Err(()), - } - } -} - -/// Enumeration of float arithmetic registers (`F`-registers) -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[repr(u8)] -#[derive(Default)] -pub enum RegF { - /// 16-bit bfloat16 format used in machine learning - #[display("f16b")] - F16B = 0, - - /// 16-bit IEEE-754 binary16 half-precision - #[display("f16")] - F16 = 1, - - /// 32-bit IEEE-754 binary32 single-precision - #[display("f32")] - F32 = 2, - - /// 64-bit IEEE-754 binary64 double-precision - #[display("f64")] - #[default] - F64 = 3, - - /// 80-bit IEEE-754 extended precision - #[display("f80")] - F80 = 4, - - /// 128-bit IEEE-754 binary128 quadruple precision - #[display("f128")] - F128 = 5, - - /// 256-bit IEEE-754 binary256 octuple precision - #[display("f256")] - F256 = 6, - - /// 512-bit tapered floating point - #[display("f512")] - F512 = 7, -} - -impl Register for RegF { - #[inline] - fn description() -> &'static str { "F register" } -} - -impl NumericRegister for RegF { - #[inline] - fn bytes(&self) -> u16 { - match self { - RegF::F16B => 2, - RegF::F16 => 2, - RegF::F32 => 4, - RegF::F64 => 8, - RegF::F80 => 10, - RegF::F128 => 16, - RegF::F256 => 32, - RegF::F512 => 64, - } - } - - #[inline] - fn layout(&self) -> number::Layout { - let fl = match self { - RegF::F16B => number::FloatLayout::BFloat16, - RegF::F16 => number::FloatLayout::IeeeHalf, - RegF::F32 => number::FloatLayout::IeeeSingle, - RegF::F64 => number::FloatLayout::IeeeDouble, - RegF::F80 => number::FloatLayout::X87DoubleExt, - RegF::F128 => number::FloatLayout::IeeeQuad, - RegF::F256 => number::FloatLayout::IeeeOct, - RegF::F512 => number::FloatLayout::FloatTapered, - }; - number::Layout::float(fl) - } -} - -impl RegF { - /// Set of all F registers - pub const ALL: [RegF; 8] = [ - RegF::F16B, - RegF::F16, - RegF::F32, - RegF::F64, - RegF::F80, - RegF::F128, - RegF::F256, - RegF::F512, - ]; - - /// Constructs [`RegF`] object for a provided requirement for register bit size - pub fn with(bits: u16, use_bfloat16: bool) -> Option { - Some(match bits { - 16 => { - if use_bfloat16 { - RegF::F16B - } else { - RegF::F16 - } - } - 32 => RegF::F32, - 64 => RegF::F64, - 80 => RegF::F80, - 128 => RegF::F128, - 256 => RegF::F256, - 512 => RegF::F512, - _ => return None, - }) - } -} - -impl From<&RegF> for u3 { - fn from(regf: &RegF) -> Self { u3::with(*regf as u8) } -} - -impl From for u3 { - fn from(regf: RegF) -> Self { u3::with(regf as u8) } -} - -impl From for RegF { - fn from(val: u3) -> Self { - match val { - v if v == RegF::F16B.into() => RegF::F16B, - v if v == RegF::F16.into() => RegF::F16, - v if v == RegF::F32.into() => RegF::F32, - v if v == RegF::F64.into() => RegF::F64, - v if v == RegF::F80.into() => RegF::F80, - v if v == RegF::F128.into() => RegF::F128, - v if v == RegF::F256.into() => RegF::F256, - v if v == RegF::F512.into() => RegF::F512, - _ => unreachable!(), - } - } -} - -impl TryFrom for RegF { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { value.reg_f().ok_or(()) } -} - -/// Enumeration of the set of general registers (`R`-registers: non-arithmetic registers, mostly -/// used for cryptography) -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[repr(u8)] -#[derive(Default)] -pub enum RegR { - /// 128-bit non-arithmetics register - #[display("r128")] - R128 = 0, - - /// 160-bit non-arithmetics register - #[display("r160")] - R160 = 1, - - /// 256-bit non-arithmetics register - #[display("r256")] - #[default] - R256 = 2, - - /// 512-bit non-arithmetics register - #[display("r512")] - R512 = 3, - - /// 1024-bit non-arithmetics register - #[display("r1024")] - R1024 = 4, - - /// 2048-bit non-arithmetics register - #[display("r2048")] - R2048 = 5, - - /// 4096-bit non-arithmetics register - #[display("r4096")] - R4096 = 6, - - /// 8192-bit non-arithmetics register - #[display("r8192")] - R8192 = 7, -} - -impl Register for RegR { - #[inline] - fn description() -> &'static str { "R register" } -} - -impl NumericRegister for RegR { - #[inline] - fn bytes(&self) -> u16 { - match self { - RegR::R128 => 16, - RegR::R160 => 20, - RegR::R256 => 32, - RegR::R512 => 64, - RegR::R1024 => 128, - RegR::R2048 => 256, - RegR::R4096 => 512, - RegR::R8192 => 1024, - } - } - - #[inline] - fn layout(&self) -> number::Layout { number::Layout::unsigned(self.bytes()) } -} - -impl RegR { - /// Set of all R registers - pub const ALL: [RegR; 8] = [ - RegR::R128, - RegR::R160, - RegR::R256, - RegR::R512, - RegR::R1024, - RegR::R2048, - RegR::R4096, - RegR::R8192, - ]; - - /// Constructs [`RegR`] object for a provided requirement for register bit size - #[inline] - pub fn with(bits: u16) -> Option { - Some(match bits { - 128 => RegR::R128, - 160 => RegR::R160, - 256 => RegR::R256, - 512 => RegR::R512, - 1024 => RegR::R1024, - 2048 => RegR::R2048, - 4096 => RegR::R4096, - 8192 => RegR::R8192, - _ => return None, - }) - } -} - -impl From<&RegR> for u3 { - fn from(regr: &RegR) -> Self { u3::with(*regr as u8) } -} - -impl From for u3 { - fn from(regr: RegR) -> Self { u3::with(regr as u8) } -} - -impl From for RegR { - fn from(val: u3) -> Self { - match val { - v if v == RegR::R128.into() => RegR::R128, - v if v == RegR::R160.into() => RegR::R160, - v if v == RegR::R256.into() => RegR::R256, - v if v == RegR::R512.into() => RegR::R512, - v if v == RegR::R1024.into() => RegR::R1024, - v if v == RegR::R2048.into() => RegR::R2048, - v if v == RegR::R4096.into() => RegR::R4096, - v if v == RegR::R8192.into() => RegR::R8192, - _ => unreachable!(), - } - } -} - -impl TryFrom for RegR { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { value.reg_r().ok_or(()) } -} - -/// 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)] -#[display(inner)] -pub enum RegAll { - /// Arithmetic integer registers (`A` registers) - #[from] - A(RegA), - - /// Arithmetic float registers (`F` registers) - #[from] - F(RegF), - - /// Non-arithmetic (general) registers (`R` registers) - #[from] - R(RegR), - - /// String registers (`S` registers) - S, -} - -impl Default for RegAll { - fn default() -> Self { RegAll::A(Default::default()) } -} - -impl Register for RegAll { - #[inline] - fn description() -> &'static str { "A, F, R or S register" } -} - -impl RegAll { - /// Returns inner A-register type, if any - #[inline] - pub fn reg_a(self) -> Option { - match self { - RegAll::A(a) => Some(a), - _ => None, - } - } - - /// Returns inner F-register type, if any - #[inline] - pub fn reg_f(self) -> Option { - match self { - RegAll::F(f) => Some(f), - _ => None, - } - } - - /// Returns inner R-register type, if any - #[inline] - pub fn reg_r(self) -> Option { - match self { - RegAll::R(r) => Some(r), - _ => None, - } - } - - /// Returns string describing the family of the register - #[inline] - pub fn family_name(self) -> &'static str { - match self { - RegAll::A(_) => RegA::description(), - RegAll::F(_) => RegF::description(), - RegAll::R(_) => RegR::description(), - RegAll::S => "S register", - } - } -} - -impl From<&RegA> for RegAll { - #[inline] - fn from(reg: &RegA) -> Self { Self::A(*reg) } -} - -impl From<&RegF> for RegAll { - #[inline] - fn from(reg: &RegF) -> Self { Self::F(*reg) } -} - -impl From<&RegR> for RegAll { - #[inline] - fn from(reg: &RegR) -> Self { Self::R(*reg) } -} - -impl From for RegAll { - #[inline] - fn from(reg: RegA2) -> Self { Self::A(reg.into()) } -} - -impl From<&RegA2> for RegAll { - #[inline] - fn from(reg: &RegA2) -> Self { Self::A(reg.into()) } -} - -impl From for RegAll { - #[inline] - fn from(reg: RegAF) -> Self { - match reg { - RegAF::A(a) => Self::A(a), - RegAF::F(f) => Self::F(f), - } - } -} - -impl From<&RegAF> for RegAll { - #[inline] - fn from(reg: &RegAF) -> Self { - match reg { - RegAF::A(a) => Self::A(*a), - RegAF::F(f) => Self::F(*f), - } - } -} - -impl From for RegAll { - #[inline] - fn from(reg: RegAR) -> Self { - match reg { - RegAR::A(a) => Self::A(a), - RegAR::R(r) => Self::R(r), - } - } -} - -impl From<&RegAR> for RegAll { - #[inline] - fn from(reg: &RegAR) -> Self { - match reg { - RegAR::A(a) => Self::A(*a), - RegAR::R(r) => Self::R(*r), - } - } -} - -/// Superset of all registers which value can be represented by a -/// [`crate::data::Number`]/[`crate::data::MaybeNumber`]. The superset includes `A`, `F`, and -/// `R` families of registers. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] -#[display(inner)] -pub enum RegAFR { - /// Arithmetic integer registers (`A` registers) - #[from] - A(RegA), - - /// Arithmetic float registers (`F` registers) - #[from] - F(RegF), - - /// Non-arithmetic (general) registers (`R` registers) - #[from] - R(RegR), -} - -impl Default for RegAFR { - fn default() -> Self { RegAFR::A(Default::default()) } -} - -impl Register for RegAFR { - #[inline] - fn description() -> &'static str { "A, F or R register" } -} - -impl NumericRegister for RegAFR { - #[inline] - fn bytes(&self) -> u16 { - match self { - RegAFR::A(a) => a.bytes(), - RegAFR::F(f) => f.bytes(), - RegAFR::R(r) => r.bytes(), - } - } - - #[inline] - fn layout(&self) -> number::Layout { - match self { - RegAFR::A(a) => a.layout(), - RegAFR::F(f) => f.layout(), - RegAFR::R(r) => r.layout(), - } - } -} - -impl RegAFR { - /// Returns inner A-register type, if any - #[inline] - pub fn reg_a(self) -> Option { - match self { - RegAFR::A(a) => Some(a), - _ => None, - } - } - - /// Returns inner F-register type, if any - #[inline] - pub fn reg_f(self) -> Option { - match self { - RegAFR::F(f) => Some(f), - _ => None, - } - } - - /// Returns inner R-register type, if any - #[inline] - pub fn reg_r(self) -> Option { - match self { - RegAFR::R(r) => Some(r), - _ => None, - } - } -} - -impl From<&RegA> for RegAFR { - #[inline] - fn from(reg: &RegA) -> Self { Self::A(*reg) } -} - -impl From<&RegF> for RegAFR { - #[inline] - fn from(reg: &RegF) -> Self { Self::F(*reg) } -} - -impl From<&RegR> for RegAFR { - #[inline] - fn from(reg: &RegR) -> Self { Self::R(*reg) } -} - -impl From for RegAFR { - #[inline] - fn from(reg: RegA2) -> Self { Self::A(reg.into()) } -} - -impl From<&RegA2> for RegAFR { - #[inline] - fn from(reg: &RegA2) -> Self { Self::A(reg.into()) } -} - -impl From for RegAFR { - #[inline] - fn from(reg: RegAF) -> Self { - match reg { - RegAF::A(a) => Self::A(a), - RegAF::F(f) => Self::F(f), - } - } -} - -impl From<&RegAF> for RegAFR { - #[inline] - fn from(reg: &RegAF) -> Self { - match reg { - RegAF::A(a) => Self::A(*a), - RegAF::F(f) => Self::F(*f), - } - } -} - -impl From for RegAFR { - #[inline] - fn from(reg: RegAR) -> Self { - match reg { - RegAR::A(a) => Self::A(a), - RegAR::R(r) => Self::R(r), - } - } -} - -impl From<&RegAR> for RegAFR { - #[inline] - fn from(reg: &RegAR) -> Self { - match reg { - RegAR::A(a) => Self::A(*a), - RegAR::R(r) => Self::R(*r), - } - } -} - -impl TryFrom for RegAFR { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { - match value { - RegAll::A(a) => Ok(RegAFR::A(a)), - RegAll::F(f) => Ok(RegAFR::F(f)), - RegAll::R(r) => Ok(RegAFR::R(r)), - _ => Err(()), - } - } -} - -/// Superset of `A` and `F` arithmetic registers -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] -#[display(inner)] -pub enum RegAF { - /// Arithmetic integer registers (`A` registers) - #[from] - A(RegA), - - /// Arithmetic float registers (`F` registers) - #[from] - F(RegF), -} - -impl Default for RegAF { - fn default() -> Self { RegAF::A(Default::default()) } -} - -impl Register for RegAF { - #[inline] - fn description() -> &'static str { "A or F register" } -} - -impl NumericRegister for RegAF { - #[inline] - fn bytes(&self) -> u16 { - match self { - RegAF::A(a) => a.bytes(), - RegAF::F(f) => f.bytes(), - } - } - - #[inline] - fn layout(&self) -> number::Layout { - match self { - RegAF::A(a) => a.layout(), - RegAF::F(f) => f.layout(), - } - } -} - -impl RegAF { - /// Returns inner A-register type, if any - #[inline] - pub fn reg_a(self) -> Option { - match self { - RegAF::A(a) => Some(a), - RegAF::F(_) => None, - } - } - - /// Returns inner F-register type, if any - #[inline] - pub fn reg_f(self) -> Option { - match self { - RegAF::A(_) => None, - RegAF::F(f) => Some(f), - } - } -} - -impl From<&RegAF> for u4 { - fn from(reg: &RegAF) -> Self { u4::from(*reg) } -} - -impl From for u4 { - fn from(reg: RegAF) -> Self { - match reg { - RegAF::A(a) => u4::with(u3::from(a).to_u8()), - RegAF::F(f) => u4::with(u3::from(f).to_u8() + 8), - } - } -} - -impl From for RegAF { - fn from(val: u4) -> Self { - match val.to_u8() { - 0..=7 => RegAF::A(RegA::from(u3::with(val.to_u8()))), - _ => RegAF::F(RegF::from(u3::with(val.to_u8() - 8))), - } - } -} - -impl From for RegAF { - #[inline] - fn from(reg: RegA2) -> Self { Self::A(reg.into()) } -} - -impl From<&RegA2> for RegAF { - #[inline] - fn from(reg: &RegA2) -> Self { Self::A(reg.into()) } -} - -impl TryFrom for RegAF { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { - match value { - RegAll::A(a) => Ok(RegAF::A(a)), - RegAll::F(f) => Ok(RegAF::F(f)), - _ => Err(()), - } - } -} - -/// Superset of `A` and `R` registers -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] -#[display(inner)] -pub enum RegAR { - /// Arithmetic integer registers (`A` registers) - #[from] - A(RegA), - - /// Non-arithmetic (general) registers (`R` registers) - #[from] - R(RegR), -} - -impl Default for RegAR { - fn default() -> Self { RegAR::A(Default::default()) } -} - -impl Register for RegAR { - #[inline] - fn description() -> &'static str { "A or R register" } -} - -impl NumericRegister for RegAR { - #[inline] - fn bytes(&self) -> u16 { - match self { - RegAR::A(a) => a.bytes(), - RegAR::R(r) => r.bytes(), - } - } - - #[inline] - fn layout(&self) -> number::Layout { - match self { - RegAR::A(a) => a.layout(), - RegAR::R(r) => r.layout(), - } - } -} - -impl RegAR { - /// Constructs register superset from register block and family integer representation - #[inline] - pub fn from(block: u1, reg: u3) -> Self { - match block.into_u8() { - 0 => RegAR::A(reg.into()), - 1 => RegAR::R(reg.into()), - _ => unreachable!(), - } - } - - /// Returns inner A-register type, if any - #[inline] - pub fn reg_a(self) -> Option { - match self { - RegAR::A(a) => Some(a), - RegAR::R(_) => None, - } - } - - /// Returns inner R-register type, if any - #[inline] - pub fn reg_r(self) -> Option { - match self { - RegAR::A(_) => None, - RegAR::R(r) => Some(r), - } - } -} - -impl From<&RegAR> for u4 { - fn from(reg: &RegAR) -> Self { u4::from(*reg) } -} - -impl From for u4 { - fn from(reg: RegAR) -> Self { - match reg { - RegAR::A(a) => u4::with(u3::from(a).to_u8()), - RegAR::R(r) => u4::with(u3::from(r).to_u8() + 8), - } - } -} - -impl From for RegAR { - fn from(val: u4) -> Self { - match val.to_u8() { - 0..=7 => RegAR::A(RegA::from(u3::with(val.to_u8()))), - _ => RegAR::R(RegR::from(u3::with(val.to_u8() - 8))), - } - } -} - -impl From for RegAR { - #[inline] - fn from(reg: RegA2) -> Self { Self::A(reg.into()) } -} - -impl From<&RegA2> for RegAR { - #[inline] - fn from(reg: &RegA2) -> Self { Self::A(reg.into()) } -} - -impl TryFrom for RegAR { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { - match value { - RegAll::A(a) => Ok(RegAR::A(a)), - RegAll::R(r) => Ok(RegAR::R(r)), - _ => Err(()), - } - } -} - -/// Block of registers, either integer arithmetic or non-arithmetic (general) registers -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[derive(Default)] -pub enum RegBlockAR { - /// Arithmetic integer registers (`A` registers) - #[display("a")] - #[default] - A, - - /// Non-arithmetic (generic) registers (`R` registers) - #[display("r")] - R, -} - -impl Register for RegBlockAR { - #[inline] - fn description() -> &'static str { "A or R register block" } -} - -impl RegBlockAR { - /// Converts value into specific register matching the provided bit dimension. If the register - /// with the given dimension does not exists, returns `None`. - pub fn into_reg(self, bits: u16) -> Option { - match self { - RegBlockAR::A => RegA::with(bits).map(RegAR::A), - RegBlockAR::R => RegR::with(bits).map(RegAR::R), - } - } -} - -impl TryFrom for RegBlockAR { - type Error = (); - - fn try_from(value: RegAll) -> Result { - match value { - RegAll::A(_) => Ok(RegBlockAR::A), - RegAll::F(_) => Err(()), - RegAll::R(_) => Ok(RegBlockAR::R), - RegAll::S => Err(()), - } - } -} - -/// Block of registers, either integer, float arithmetic or non-arithmetic (general) registers -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[derive(Default)] -pub enum RegBlockAFR { - /// Arithmetic integer registers (`A` registers) - #[display("a")] - #[default] - A, - - /// Arithmetic float registers (`F` registers) - #[display("f")] - F, - - /// Non-arithmetic (generic) registers (`R` registers) - #[display("r")] - R, -} - -impl Register for RegBlockAFR { - #[inline] - fn description() -> &'static str { "A, F or R register block" } -} - -impl RegBlockAFR { - /// Converts value into specific register matching the provided bit dimension. If the register - /// with the given dimension does not exists, returns `None`. - pub fn into_reg(self, bits: u16) -> Option { - match self { - RegBlockAFR::A => RegA::with(bits).map(RegAFR::A), - RegBlockAFR::F => RegF::with(bits, false).map(RegAFR::F), - RegBlockAFR::R => RegR::with(bits).map(RegAFR::R), - } - } -} - -impl TryFrom for RegBlockAFR { - type Error = (); - - fn try_from(value: RegAll) -> Result { - match value { - RegAll::A(_) => Ok(RegBlockAFR::A), - RegAll::F(_) => Ok(RegBlockAFR::F), - RegAll::R(_) => Ok(RegBlockAFR::R), - RegAll::S => Err(()), - } - } -} - -/// Blocks of registers including all non-control register types -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[derive(Default)] -pub enum RegBlock { - /// Arithmetic integer registers (`A` registers) - #[display("a")] - #[default] - A, - - /// Arithmetic float registers (`F` registers) - #[display("f")] - F, - - /// Non-arithmetic (generic) registers (`R` registers) - #[display("r")] - R, - - /// Byte-string registers (`S` registers) - #[display("s")] - S, -} - -impl Register for RegBlock { - #[inline] - fn description() -> &'static str { "A, F, R or S register block" } -} - -impl From for RegBlock { - fn from(reg: RegAll) -> Self { - match reg { - RegAll::A(_) => RegBlock::A, - RegAll::F(_) => RegBlock::F, - RegAll::R(_) => RegBlock::R, - RegAll::S => RegBlock::S, - } - } -} diff --git a/src/reg/indexes.rs b/src/reg/indexes.rs deleted file mode 100644 index c2cda28..0000000 --- a/src/reg/indexes.rs +++ /dev/null @@ -1,640 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -use core::convert::TryFrom; - -use amplify::num::error::OverflowError; -use amplify::num::{u3, u4, u5}; - -use crate::reg::{RegAll, Register}; - -/// All possible register indexes for `a` and `r` register sets -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[repr(u8)] -#[derive(Default)] -pub enum Reg32 { - /// Register with index `[0]` - #[display("[0]")] - #[default] - Reg0 = 0, - - /// Register with index `[1]` - #[display("[1]")] - Reg1 = 1, - - /// Register with index `[2]` - #[display("[2]")] - Reg2 = 2, - - /// Register with index `[3]` - #[display("[3]")] - Reg3 = 3, - - /// Register with index `[4]` - #[display("[4]")] - Reg4 = 4, - - /// Register with index `[5]` - #[display("[5]")] - Reg5 = 5, - - /// Register with index `[6]` - #[display("[6]")] - Reg6 = 6, - - /// Register with index `[7]` - #[display("[7]")] - Reg7 = 7, - - /// Register with index `[8]` - #[display("[8]")] - Reg8 = 8, - - /// Register with index `[9]` - #[display("[9]")] - Reg9 = 9, - - /// Register with index `[10]` - #[display("[10]")] - Reg10 = 10, - - /// Register with index `[11]` - #[display("[11]")] - Reg11 = 11, - - /// Register with index `[12]` - #[display("[12]")] - Reg12 = 12, - - /// Register with index `[13]` - #[display("[13]")] - Reg13 = 13, - - /// Register with index `[14]` - #[display("[14]")] - Reg14 = 14, - - /// Register with index `[15]` - #[display("[15]")] - Reg15 = 15, - - /// Register with index `[16]` - #[display("[16]")] - Reg16 = 16, - - /// Register with index `[17]` - #[display("[17]")] - Reg17 = 17, - - /// Register with index `[18]` - #[display("[18]")] - Reg18 = 18, - - /// Register with index `[19]` - #[display("[19]")] - Reg19 = 19, - - /// Register with index `[20]` - #[display("[20]")] - Reg20 = 20, - - /// Register with index `[21]` - #[display("[21]")] - Reg21 = 21, - - /// Register with index `[22]` - #[display("[22]")] - Reg22 = 22, - - /// Register with index `[23]` - #[display("[23]")] - Reg23 = 23, - - /// Register with index `[24]` - #[display("[24]")] - Reg24 = 24, - - /// Register with index `[25]` - #[display("[25]")] - Reg25 = 25, - - /// Register with index `[26]` - #[display("[26]")] - Reg26 = 26, - - /// Register with index `[27]` - #[display("[27]")] - Reg27 = 27, - - /// Register with index `[28]` - #[display("[28]")] - Reg28 = 28, - - /// Register with index `[29]` - #[display("[29]")] - Reg29 = 29, - - /// Register with index `[30]` - #[display("[30]")] - Reg30 = 30, - - /// Register with index `[31]` - #[display("[31]")] - Reg31 = 31, -} - -impl Reg32 { - /// Constant enumerating all register indexes. - pub const ALL: [Reg32; 32] = [ - Reg32::Reg0, - Reg32::Reg1, - Reg32::Reg2, - Reg32::Reg3, - Reg32::Reg4, - Reg32::Reg5, - Reg32::Reg6, - Reg32::Reg7, - Reg32::Reg8, - Reg32::Reg9, - Reg32::Reg10, - Reg32::Reg11, - Reg32::Reg12, - Reg32::Reg13, - Reg32::Reg14, - Reg32::Reg15, - Reg32::Reg16, - Reg32::Reg17, - Reg32::Reg18, - Reg32::Reg19, - Reg32::Reg20, - Reg32::Reg21, - Reg32::Reg22, - Reg32::Reg23, - Reg32::Reg24, - Reg32::Reg25, - Reg32::Reg26, - Reg32::Reg27, - Reg32::Reg28, - Reg32::Reg29, - Reg32::Reg30, - Reg32::Reg31, - ]; - - /// Returns `usize` representation of the register index - #[inline] - pub fn to_usize(self) -> usize { self as u8 as usize } -} - -impl Register for Reg32 { - #[inline] - fn description() -> &'static str { "5-bit register index" } -} - -impl From<&Reg32> for u5 { - #[inline] - fn from(reg32: &Reg32) -> Self { u5::with(*reg32 as u8) } -} - -impl From for u5 { - #[inline] - fn from(reg32: Reg32) -> Self { u5::with(reg32 as u8) } -} - -impl From<&Reg32> for u8 { - #[inline] - fn from(reg32: &Reg32) -> Self { *reg32 as u8 } -} - -impl From for u8 { - #[inline] - fn from(reg32: Reg32) -> Self { reg32 as u8 } -} - -impl From<&Reg32> for Reg32 { - #[inline] - fn from(reg32: &Reg32) -> Self { *reg32 } -} - -impl From for Reg32 { - fn from(val: u5) -> Self { - match val { - v if v == Reg32::Reg0.into() => Reg32::Reg0, - v if v == Reg32::Reg1.into() => Reg32::Reg1, - v if v == Reg32::Reg2.into() => Reg32::Reg2, - v if v == Reg32::Reg3.into() => Reg32::Reg3, - v if v == Reg32::Reg4.into() => Reg32::Reg4, - v if v == Reg32::Reg5.into() => Reg32::Reg5, - v if v == Reg32::Reg6.into() => Reg32::Reg6, - v if v == Reg32::Reg7.into() => Reg32::Reg7, - v if v == Reg32::Reg8.into() => Reg32::Reg8, - v if v == Reg32::Reg9.into() => Reg32::Reg9, - v if v == Reg32::Reg10.into() => Reg32::Reg10, - v if v == Reg32::Reg11.into() => Reg32::Reg11, - v if v == Reg32::Reg12.into() => Reg32::Reg12, - v if v == Reg32::Reg13.into() => Reg32::Reg13, - v if v == Reg32::Reg14.into() => Reg32::Reg14, - v if v == Reg32::Reg15.into() => Reg32::Reg15, - v if v == Reg32::Reg16.into() => Reg32::Reg16, - v if v == Reg32::Reg17.into() => Reg32::Reg17, - v if v == Reg32::Reg18.into() => Reg32::Reg18, - v if v == Reg32::Reg19.into() => Reg32::Reg19, - v if v == Reg32::Reg20.into() => Reg32::Reg20, - v if v == Reg32::Reg21.into() => Reg32::Reg21, - v if v == Reg32::Reg22.into() => Reg32::Reg22, - v if v == Reg32::Reg23.into() => Reg32::Reg23, - v if v == Reg32::Reg24.into() => Reg32::Reg24, - v if v == Reg32::Reg25.into() => Reg32::Reg25, - v if v == Reg32::Reg26.into() => Reg32::Reg26, - v if v == Reg32::Reg27.into() => Reg32::Reg27, - v if v == Reg32::Reg28.into() => Reg32::Reg28, - v if v == Reg32::Reg29.into() => Reg32::Reg29, - v if v == Reg32::Reg30.into() => Reg32::Reg30, - v if v == Reg32::Reg31.into() => Reg32::Reg31, - _ => unreachable!(), - } - } -} - -/// Shorter version of possible register indexes for `a` and `r` register sets -/// covering initial 16 registers -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[repr(u8)] -#[derive(Default)] -pub enum Reg16 { - /// Register with index `[0]` - #[display("[0]")] - #[default] - Reg0 = 0, - - /// Register with index `[1]` - #[display("[1]")] - Reg1 = 1, - - /// Register with index `[2]` - #[display("[2]")] - Reg2 = 2, - - /// Register with index `[3]` - #[display("[3]")] - Reg3 = 3, - - /// Register with index `[4]` - #[display("[4]")] - Reg4 = 4, - - /// Register with index `[5]` - #[display("[5]")] - Reg5 = 5, - - /// Register with index `[6]` - #[display("[6]")] - Reg6 = 6, - - /// Register with index `[7]` - #[display("[7]")] - Reg7 = 7, - - /// Register with index `[8]` - #[display("[8]")] - Reg8 = 8, - - /// Register with index `[9]` - #[display("[9]")] - Reg9 = 9, - - /// Register with index `[10]` - #[display("[10]")] - Reg10 = 10, - - /// Register with index `[11]` - #[display("[11]")] - Reg11 = 11, - - /// Register with index `[12]` - #[display("[12]")] - Reg12 = 12, - - /// Register with index `[13]` - #[display("[13]")] - Reg13 = 13, - - /// Register with index `[14]` - #[display("[14]")] - Reg14 = 14, - - /// Register with index `[15]` - #[display("[15]")] - Reg15 = 15, -} - -impl Reg16 { - /// Constant enumerating all register indexes. - pub const ALL: [Reg16; 16] = [ - Reg16::Reg0, - Reg16::Reg1, - Reg16::Reg2, - Reg16::Reg3, - Reg16::Reg4, - Reg16::Reg5, - Reg16::Reg6, - Reg16::Reg7, - Reg16::Reg8, - Reg16::Reg9, - Reg16::Reg10, - Reg16::Reg11, - Reg16::Reg12, - Reg16::Reg13, - Reg16::Reg14, - Reg16::Reg15, - ]; -} - -impl Register for Reg16 { - #[inline] - fn description() -> &'static str { "4-bit register index" } -} - -impl From<&Reg16> for u4 { - #[inline] - fn from(reg16: &Reg16) -> Self { u4::with(*reg16 as u8) } -} - -impl From for u4 { - #[inline] - fn from(reg16: Reg16) -> Self { u4::with(reg16 as u8) } -} - -impl From for Reg16 { - fn from(val: u4) -> Self { - match val { - v if v == Reg16::Reg0.into() => Reg16::Reg0, - v if v == Reg16::Reg1.into() => Reg16::Reg1, - v if v == Reg16::Reg2.into() => Reg16::Reg2, - v if v == Reg16::Reg3.into() => Reg16::Reg3, - v if v == Reg16::Reg4.into() => Reg16::Reg4, - v if v == Reg16::Reg5.into() => Reg16::Reg5, - v if v == Reg16::Reg6.into() => Reg16::Reg6, - v if v == Reg16::Reg7.into() => Reg16::Reg7, - v if v == Reg16::Reg8.into() => Reg16::Reg8, - v if v == Reg16::Reg9.into() => Reg16::Reg9, - v if v == Reg16::Reg10.into() => Reg16::Reg10, - v if v == Reg16::Reg11.into() => Reg16::Reg11, - v if v == Reg16::Reg12.into() => Reg16::Reg12, - v if v == Reg16::Reg13.into() => Reg16::Reg13, - v if v == Reg16::Reg14.into() => Reg16::Reg14, - v if v == Reg16::Reg15.into() => Reg16::Reg15, - _ => unreachable!(), - } - } -} - -impl From for Reg32 { - #[inline] - fn from(reg16: Reg16) -> Self { u5::with(reg16 as u8).into() } -} - -impl From<&Reg16> for Reg32 { - #[inline] - fn from(reg16: &Reg16) -> Self { u5::with(*reg16 as u8).into() } -} - -impl TryFrom for Reg16 { - type Error = OverflowError; - - fn try_from(value: Reg32) -> Result { - u4::try_from(value as u8).map(Reg16::from) - } -} - -/// Short version of register indexes for `a` and `r` register sets covering -/// initial 8 registers only -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[repr(u8)] -#[derive(Default)] -pub enum Reg8 { - /// Register with index `[0]` - #[display("[0]")] - #[default] - Reg0 = 0, - - /// Register with index `[1]` - #[display("[1]")] - Reg1 = 1, - - /// Register with index `[2]` - #[display("[2]")] - Reg2 = 2, - - /// Register with index `[3]` - #[display("[3]")] - Reg3 = 3, - - /// Register with index `[4]` - #[display("[4]")] - Reg4 = 4, - - /// Register with index `[5]` - #[display("[5]")] - Reg5 = 5, - - /// Register with index `[6]` - #[display("[6]")] - Reg6 = 6, - - /// Register with index `[7]` - #[display("[7]")] - Reg7 = 7, -} - -impl Reg8 { - /// Constant enumerating all register indexes. - pub const ALL: [Reg8; 8] = [ - Reg8::Reg0, - Reg8::Reg1, - Reg8::Reg2, - Reg8::Reg3, - Reg8::Reg4, - Reg8::Reg5, - Reg8::Reg6, - Reg8::Reg7, - ]; -} - -impl Register for Reg8 { - #[inline] - fn description() -> &'static str { "3-bit register index" } -} - -impl From<&Reg8> for u3 { - #[inline] - fn from(reg8: &Reg8) -> Self { u3::with(*reg8 as u8) } -} - -impl From for u3 { - #[inline] - fn from(reg8: Reg8) -> Self { u3::with(reg8 as u8) } -} - -impl From for Reg8 { - fn from(val: u3) -> Self { - match val { - v if v == Reg8::Reg0.into() => Reg8::Reg0, - v if v == Reg8::Reg1.into() => Reg8::Reg1, - v if v == Reg8::Reg2.into() => Reg8::Reg2, - v if v == Reg8::Reg3.into() => Reg8::Reg3, - v if v == Reg8::Reg4.into() => Reg8::Reg4, - v if v == Reg8::Reg5.into() => Reg8::Reg5, - v if v == Reg8::Reg6.into() => Reg8::Reg6, - v if v == Reg8::Reg7.into() => Reg8::Reg7, - _ => unreachable!(), - } - } -} - -impl From for Reg32 { - #[inline] - fn from(reg8: Reg8) -> Self { u5::with(reg8 as u8).into() } -} - -impl From<&Reg8> for Reg32 { - #[inline] - fn from(reg8: &Reg8) -> Self { u5::with(*reg8 as u8).into() } -} - -impl TryFrom for Reg8 { - type Error = OverflowError; - - fn try_from(value: Reg32) -> Result { - u3::try_from(value as u8).map(Reg8::from) - } -} - -/// Possible index values for string registers (`S`-registers). -/// -/// For `S`-registers it is possible to denote index as `u4` value, with the real index equal to -/// this value modulo 32. This is required because of the bit size parameters for the string -/// opcode arguments. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] -#[display("s16[{0}]")] -pub struct RegS(#[from] u4); - -impl RegS { - /// Returns `u8` value corresponding to the register number - #[inline] - pub fn as_u8(self) -> u8 { self.0.to_u8() } - - /// Returns `usize` value corresponding to the register number - #[inline] - pub fn as_usize(self) -> usize { self.0.to_u8() as usize } -} - -impl Register for RegS { - #[inline] - fn description() -> &'static str { "4-bit S register index" } -} - -impl Default for RegS { - #[inline] - fn default() -> Self { RegS(u4::MIN) } -} - -impl From for u8 { - #[inline] - fn from(reg: RegS) -> Self { reg.0.to_u8() } -} - -impl From<&RegS> for u8 { - #[inline] - fn from(reg: &RegS) -> Self { reg.0.to_u8() } -} - -impl From for usize { - #[inline] - fn from(reg: RegS) -> Self { reg.0.to_u8() as usize } -} - -impl From for RegS { - #[inline] - fn from(val: u8) -> Self { RegS(u4::with(val % 16)) } -} - -impl From<&u4> for RegS { - #[inline] - fn from(val: &u4) -> Self { RegS(*val) } -} - -impl From for u4 { - #[inline] - fn from(reg: RegS) -> Self { reg.0 } -} - -impl From<&RegS> for u4 { - #[inline] - fn from(reg: &RegS) -> Self { reg.0 } -} - -impl From for RegS { - #[inline] - fn from(val: u5) -> Self { RegS(u4::with(val.to_u8() % 16)) } -} - -impl From<&u5> for RegS { - #[inline] - fn from(val: &u5) -> Self { RegS(u4::with(val.to_u8() % 16)) } -} - -impl From for u5 { - #[inline] - fn from(reg: RegS) -> Self { u5::with(reg.0.to_u8()) } -} - -impl From<&RegS> for u5 { - #[inline] - 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; - - fn try_from(value: Reg32) -> Result { - u5::try_from(value as u8).map(RegS::from) - } -} - -impl TryFrom for RegS { - type Error = (); - - #[inline] - fn try_from(value: RegAll) -> Result { - match value { - RegAll::S => Ok(RegS(u4::with(0))), - _ => Err(()), - } - } -} diff --git a/src/reg/mod.rs b/src/reg/mod.rs deleted file mode 100644 index c6ee05d..0000000 --- a/src/reg/mod.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// 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. - -//! AluVM registers system - -mod core_regs; -mod families; -mod indexes; - -pub use core_regs::{CoreRegs, CALL_STACK_SIZE}; -pub use families::{ - NumericRegister, RegA, RegA2, RegAF, RegAFR, RegAR, RegAll, RegBlock, RegBlockAFR, RegBlockAR, - RegF, RegR, -}; -pub use indexes::{Reg16, Reg32, Reg8, RegS}; - -/// Trait marking all types representing register family, specific register or register index -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(), - } - } -} diff --git a/src/stl.rs b/src/stl.rs index 14fb97b..83899f3 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -3,39 +3,35 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. //! Strict types library generator methods. -use core::convert::TryFrom; - use strict_types::typelib::{CompileError, LibBuilder}; use strict_types::TypeLib; -use crate::library::{Lib, LibSite}; -use crate::LIB_NAME_ALUVM; +use crate::{CoreConfig, Lib, LibSite, LIB_NAME_ALUVM}; -/// Strict type id for the library providing data types from this crate. +/// Strict type id for the lib-old providing data types from this crate. pub const LIB_ID_ALUVM: &str = - "stl:YnA1fyHl-46C2hsC-tZPxn60-w1jLk5Y-rDTjRmy-xo!5xjY#congo-archive-folio"; + "stl:t1kptI_t-R8Ei0Wa-e0m53SK-toGi5AC-si8GK5F-MbQp588#reward-accent-swim"; fn _aluvm_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_ALUVM), tiny_bset! { @@ -44,11 +40,12 @@ fn _aluvm_stl() -> Result { }) .transpile::() .transpile::() + .transpile::() .compile() } -/// Generates strict type library providing data types from this crate. -pub fn aluvm_stl() -> TypeLib { _aluvm_stl().expect("invalid strict type AluVM library") } +/// Generates strict type lib-old providing data types from this crate. +pub fn aluvm_stl() -> TypeLib { _aluvm_stl().expect("invalid strict type AluVM lib-old") } #[cfg(test)] mod test { diff --git a/src/vm.rs b/src/vm.rs index 75dd3cf..6fb9d45 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -3,59 +3,62 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2021-2024 by -// Dr Maxim Orlovsky +// Designed in 2021-2025 by Dr Maxim Orlovsky +// Written in 2021-2025 by Dr Maxim Orlovsky // -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Labs, -// Institute for Distributed and Cognitive Computing, Switzerland. -// All rights reserved. +// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland. +// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), +// Institute for Distributed and Cognitive Systems (InDCS), Switzerland. +// Copyright (C) 2021-2025 Dr Maxim Orlovsky. +// All rights under the above copyrights are 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 +// 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 +// 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. +// 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. //! Alu virtual machine -#[cfg(feature = "alloc")] -use alloc::boxed::Box; use core::marker::PhantomData; -use crate::isa::{Instr, InstructionSet, ReservedOp}; +use crate::core::{Core, CoreConfig, CoreExt, Status}; +use crate::isa::{Instr, Instruction}; use crate::library::{Lib, LibId, LibSite}; -use crate::reg::CoreRegs; /// Alu virtual machine providing single-core execution environment #[derive(Clone, Debug, Default)] -pub struct Vm> -where Isa: InstructionSet +pub struct Vm> +where Isa: Instruction { /// A set of registers - pub registers: Box, + pub core: Core, phantom: PhantomData, } /// Runtime for program execution. impl Vm -where Isa: InstructionSet +where Isa: Instruction { - /// Constructs new virtual machine instance. - pub fn new() -> Self { + /// Constructs new virtual machine instance with default core configuration. + pub fn new() -> Self { Self { core: Core::new(), phantom: Default::default() } } + + /// Constructs new virtual machine instance with default core configuration. + pub fn with(config: CoreConfig, cx_config: ::Config) -> Self { Self { - registers: Box::default(), + core: Core::with(config, cx_config), phantom: Default::default(), } } + /// Resets all registers of the VM except those which were set up with the config object. + pub fn reset(&mut self) { self.core.reset(); } + /// Executes the program starting from the provided entry point. /// /// # Returns @@ -64,19 +67,19 @@ where Isa: InstructionSet pub fn exec<'prog>( &mut self, entry_point: LibSite, - lib_resolver: impl Fn(LibId) -> Option<&'prog Lib>, context: &Isa::Context<'_>, - ) -> bool { + lib_resolver: impl Fn(LibId) -> Option<&'prog Lib>, + ) -> Status { let mut call = Some(entry_point); while let Some(ref mut site) = call { - if let Some(lib) = lib_resolver(site.lib) { - call = lib.exec::(site.pos, &mut self.registers, context); - } else if let Some(pos) = site.pos.checked_add(1) { - site.pos = pos; + if let Some(lib) = lib_resolver(site.lib_id) { + call = lib.exec::(site.offset, &mut self.core, context); + } else if let Some(pos) = site.offset.checked_add(1) { + site.offset = pos; } else { call = None; }; } - self.registers.st0 + self.core.ck() } } diff --git a/stl/AluVM@0.1.0.sta b/stl/AluVM@0.1.0.sta index 689ac94..a6ce795 100644 --- a/stl/AluVM@0.1.0.sta +++ b/stl/AluVM@0.1.0.sta @@ -1,19 +1,20 @@ -----BEGIN STRICT TYPE LIB----- -Id: stl:YnA1fyHl-46C2hsC-tZPxn60-w1jLk5Y-rDTjRmy-xo!5xjY#congo-archive-folio +Id: stl:t1kptI_t-R8Ei0Wa-e0m53SK-toGi5AC-si8GK5F-MbQp588#reward-accent-swim Name: AluVM -Dependencies: Std#ralph-blue-lucky -Check-SHA256: 33bd3f759d49fd0e46f3179acda055dcc81082b29db36d949482f9e168b6781c +Dependencies: Std#delete-roman-hair +Check-SHA256: 14b68ec0fe5d0af2cfe20b9d141c5dbce404dfb3b7e694581f930dac7697c649 -1wm|eR!srQEFN!zncXl9K5w2;FV{y1jDTJCC^p$-mHEbO0#qjhQ*>kj15cB -r>9x-Cs#sheE97?nsOaXHkb)PY;b5{Lt$`pdPjz(4^OqB|1Bk8zGh-IHIi*o-3_)yg -XkkNPaC1&|Z3X}bNpoRNVQpmv0RRO80?I5NZ-bfLFbqC#o>4E?M+l67UG^w8*<_XZ#%uyqCnto_jB5_Y -Jg;9E|1`d*r&;qSS3+uh`0YNLave-Im;eX@$}AplgPGkh3_fq3Q7_j=2#kPT_9!;lWR>~GYywm#dPjz( -4^OqB|1Bk8zGh-IHIi*o-0RR91000002LJ#70000021#>aQ)Oob0RRaBlI1_jdG2&s -5l|&f*ZHF;qIbkhZU7>Kq8jXl$Dv2x0000000000KmY&$00000159aR1_T6Yb75rxvkfUR&BN*@p#4(i -bP}+ptVxWdH~O06+i$000000096000000000DJVRT^t2mk;;0000000000 -|Nj60000001Z-(ya{6isfX<|ua1pxpD002NB00vBH -VpC;j1pxpF0jDrk0xkJm$nc4yMWR2J-cc#Q6SofWC)gp7L6!Sc3IG5A000000RI300000000&HIVpC~! -Wd;HRY-wTvr!Z9lE%{u?@QI^EqCb}2Q7OO^w+`_q*ddTXmHSf)18{G10006 +1wm|eR!sqdiR(=d3vg7gbV~*3!PlK51EySK%g?1}nECovJTYnmQ*>kj15|Z*pZrZ*FF3X9fZUXkl!00)mO_O%DrjRIhYP1?a)oog)LLTw}}6rDvG=`c^zKYGH;V(R;4& +W&+>mb;*F>vukd;=m`ygb@x#_>`RmOO$}pjZE$R5cxiNbOlfTZ1OfmAZf|a7000011aog~WdH>M000OD +NpoRIWCZ~L1p)$siR(=d3vg7gbV~*3!PlK51EySK%g?1}nECovJTYo|M~0;jPqm@t3InIR0Ny%Ft`YGA +h^_-OV-~qNrBQ4E2m*qM>rD>}a8$2!O9kk`*PSB+rd(so&!uOW`TABoF=~28hNTZrwV~w-1E;$H-a1RJ +5%B|vt^+e;7P&d4QEUJJ00000000jF000000009_X<`Nh1Zi_&WdI2QWv$_rj1;Ln94Z{?gZ&kLW~lle +DM+%Me^-~ElJ>=!0000000000{{R30000001Y>VxWdH~O06+i$000000096000000000DJVRT^t2mk;; +0000000000|Nj60000001Z-(ya{vher!Z9lE%{u?@QI^EqCb}2Q7OO^w+`_q*ddTXmHSf)0000000000 +{{R30000001x#sTNn`~900#g7Kp+4IOle|MX>?@<0tIYoVo78Hr!Z9lE%{u?@QI^EqCb}2Q7OO^w+`_q +*ddTXmHSf)25)9&b7gb@00I -----END STRICT TYPE LIB----- diff --git a/stl/AluVM@0.1.0.stl b/stl/AluVM@0.1.0.stl index 3007346..52e267d 100644 Binary files a/stl/AluVM@0.1.0.stl and b/stl/AluVM@0.1.0.stl differ diff --git a/stl/AluVM@0.1.0.sty b/stl/AluVM@0.1.0.sty index 88c4962..9c786e3 100644 --- a/stl/AluVM@0.1.0.sty +++ b/stl/AluVM@0.1.0.sty @@ -1,40 +1,39 @@ {- - Id: stl:YnA1fyHl-46C2hsC-tZPxn60-w1jLk5Y-rDTjRmy-xo!5xjY#congo-archive-folio + Id: stl:t1kptI_t-R8Ei0Wa-e0m53SK-toGi5AC-si8GK5F-MbQp588#reward-accent-swim Name: AluVM Version: 0.1.0 Description: AluVM data type library Author: Dr Maxim Orlovsky - Copyright (C) 2023-2024 UBIDECO Institute. All rights reserved. + Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO), + Institute for Distributed and Cognitive Systems (InDCS), Switzerland. + Copyright (C) 2021-2025 Dr Maxim Orlovsky. License: Apache-2.0 -} @context typelib AluVM -import Std#ralph-blue-lucky - use AlphaCaps#picnic-soprano-aurora +import Std#delete-roman-hair + use Bool#oxygen-complex-duet use AlphaCapsNum#aladdin-zebra-marble -@mnemonic(taboo-olympic-cloud) -data IsaName : Std.AlphaCaps, [Std.AlphaCapsNum ^ 1..0x7] +@mnemonic(ventura-ibiza-special) +data CoreConfig : halt Std.Bool, complexityLim U64? -@mnemonic(freedom-ship-canal) -data IsaSeg : {IsaName ^ ..0x40} +@mnemonic(mobile-letter-absorb) +data IsaId : Std.AlphaCapsNum, [Std.AlphaCapsNum ^ ..0xf] -@mnemonic(bernard-igor-version) -data Lib : isae IsaSeg +@mnemonic(pinball-legend-camel) +data Lib : isae {IsaId ^ ..0xff} , code [Byte] , data [Byte] - , libs LibSeg + , libs {LibId ^ ..0xff} @mnemonic(germany-culture-olivia) data LibId : [Byte ^ 32] -@mnemonic(gilbert-tomato-caesar) -data LibSeg : {LibId ^ ..0xff} - -@mnemonic(chance-agent-ivory) -data LibSite : lib LibId, pos U16 +@mnemonic(friend-beatles-carlo) +data LibSite : libId LibId, offset U16 diff --git a/tests/depCargo.toml b/tests/depCargo.toml index 27fd15e..cb0aca6 100644 --- a/tests/depCargo.toml +++ b/tests/depCargo.toml @@ -1,4 +1,4 @@ -# This is an add-on that must be added to any dependency using this library +# This is an add-on that must be added to any dependency using this lib-old alure = { path = ".." }