diff --git a/src/libvm/.gitignore b/src/libvm/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/src/libvm/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/src/libvm/Cargo.lock b/src/libvm/Cargo.lock new file mode 100644 index 0000000..554aab8 --- /dev/null +++ b/src/libvm/Cargo.lock @@ -0,0 +1,851 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bincode" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bstr" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cactus" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfgrammar" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "vob 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "chrono" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "doc-comment" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "error-chain" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "filetime" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getset" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-error 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "globset" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hermit-abi" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ignore" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indexmap" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libvm" +version = "0.1.0" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfgrammar 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lrlex 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lrpar 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rerun_except 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "snafu 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lrlex" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lrpar 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typename 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lrpar" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cactus 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfgrammar 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lrtable 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "packedvec 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rmp-serde 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typename 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "vergen 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "vob 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lrtable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfgrammar 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "macro-attr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "newtype_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sparsevec 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "vob 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "macro-attr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "newtype_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "packedvec" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-error" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-error-attr 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rerun_except" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ignore 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rmp" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rmp-serde" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rmp 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "snafu" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "snafu-derive 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "snafu-derive" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sparsevec" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "packedvec 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "vob 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "try_from" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typename" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typename_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typename_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vergen" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "getset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vob" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum bstr 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum cactus 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0713c9ecac2733e4c61a93157674eee620ba377d263714a6083d7a8be13468eb" +"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum cfgrammar 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20f2d944e00cc7ce8af4feb1604b0b35d846d9d44f526de3b6eb754190e9a901" +"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" +"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" +"checksum filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +"checksum getset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f62a139c59ae846c3964c392f12aac68f1997d1a40e9d3b40a89a4ab553e04a0" +"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +"checksum ignore 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "522daefc3b69036f80c7d2990b28ff9e0471c683bad05ca258e0a01dd22c5a1e" +"checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum lrlex 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "205bebfb37f228ad711fc22944328a3f2b9d89acb30590cbae6005c3aa2153dd" +"checksum lrpar 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5df95ed1526bb6a2d9e3890de679fc17178f5e68e930d921926d9e6e4e673638" +"checksum lrtable 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35afa92a7b9135277668b9a696ad9811238196f0b5fb8bda79a6f343fe3ef044" +"checksum macro-attr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00e51c6f0e2bf862b01b3d784fc32b02feb248a69062c51fb0b6d14cd526cc2a" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +"checksum newtype_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ac8cd24d9f185bb7223958d8c1ff7a961b74b1953fd05dba7cc568a63b3861ec" +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum packedvec 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1fce532538ba443dbd7e9d0dd16e687cec205dc1c13fa4e6cb225f3405d85cdd" +"checksum proc-macro-error 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a" +"checksum proc-macro-error-attr 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a" +"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +"checksum regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" +"checksum rerun_except 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f74370ce9660eea246c8c15aa4e30e836438a3ff9dc24af4ae1d3c6c6a0a3c1e" +"checksum rmp 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "0f10b46df14cf1ee1ac7baa4d2fbc2c52c0622a4b82fa8740e37bc452ac0184f" +"checksum rmp-serde 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4c1ee98f14fe8b8e9c5ea13d25da7b2a1796169202c57a09d7288de90d56222b" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +"checksum snafu 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "546db9181bce2aa22ed883c33d65603b76335b4c2533a98289f54265043de7a1" +"checksum snafu-derive 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bdc75da2e0323f297402fd9c8fdba709bb04e4c627cbe31d19a2c91fc8d9f0e2" +"checksum sparsevec 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a182a1fc36753f8a2e3eea04cc3cd28065d2949cbda1e3a453cd183dace42bbb" +"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum structopt 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe43617218c0805c6eb37160119dc3c548110a67786da7218d1c6555212f073" +"checksum structopt-derive 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd" +"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +"checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" +"checksum typename 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c255dd1af8cf5cfb95f062266201778080d215e86294dba14c47bf3137c55419" +"checksum typename_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c68acf2f6e8a32e2bad47e73ee177558583fb9dbb264a5c0569c7f9f80f79b0a" +"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum vergen 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "93fb2d57fbc535fcd45548c99b141d2d960995daaf04b864c4d9fe1ea011c819" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +"checksum vob 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5430ea8977e2f0ed62cd37239cd713415022b240095c2f9187c32823015ae52f" +"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/src/libvm/Cargo.toml b/src/libvm/Cargo.toml new file mode 100644 index 0000000..b572b43 --- /dev/null +++ b/src/libvm/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "libvm" +version = "0.1.0" +authors = ["Alek Ratzloff "] +edition = "2018" + +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +structopt = "0.3" +bitflags = "1" +byteorder = "1" +lazy_static = "1" +snafu = "0.6.2" + +cfgrammar = "0.6" +lrlex = "0.6" +lrpar = "0.6" +regex = "*" + +[build-dependencies] +cfgrammar = "0.6" +lrlex = "0.6" +lrpar = "0.6" +rerun_except = "0.1" diff --git a/src/libvm/build.rs b/src/libvm/build.rs new file mode 100644 index 0000000..22a5461 --- /dev/null +++ b/src/libvm/build.rs @@ -0,0 +1,18 @@ +use cfgrammar::yacc::YaccKind; +use lrlex::LexerBuilder; +use lrpar::{CTParserBuilder}; +use rerun_except::rerun_except; + +fn main() -> Result<(), Box> { + let lex_rule_ids_map = CTParserBuilder::new() + .yacckind(YaccKind::Grmtools) + .process_file_in_src("obj/syn/parser.y")?; + LexerBuilder::new() + .rule_ids_map(lex_rule_ids_map) + .process_file_in_src("obj/syn/lexer.l")?; + rerun_except(&[ + "examples/*.asm", + "tests/*.asm", + ]).unwrap(); + Ok(()) +} diff --git a/src/libvm/src/addr.rs b/src/libvm/src/addr.rs new file mode 100644 index 0000000..e13ab95 --- /dev/null +++ b/src/libvm/src/addr.rs @@ -0,0 +1,73 @@ +use std::{ + cmp::Ordering, + fmt::{self, Formatter, LowerHex}, + ops::{Add, AddAssign}, +}; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Addr(pub u64); + +impl LowerHex for Addr { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + let Addr(v) = self; + LowerHex::fmt(v, fmt) + } +} + +impl Add for Addr +where + T: Add, + u64: Add, +{ + type Output = Addr; + + fn add(self, rhs: T) -> Self::Output { + Addr(self.0 + rhs) + } +} + +macro_rules! impl_add_assign { + ($ty:ty) => { + impl AddAssign<$ty> for Addr { + fn add_assign(&mut self, rhs: $ty) { + self.0 = self.0 + (rhs as u64); + } + } + } +} + +impl_add_assign!(usize); +impl_add_assign!(u64); + +macro_rules! impl_cmp { + ($ty:ty) => { + impl PartialEq<$ty> for Addr { + fn eq(&self, other: &$ty) -> bool { + self.0 == (*other as u64) + } + } + + impl PartialOrd<$ty> for Addr { + fn partial_cmp(&self, other: &$ty) -> Option { + let other = *other as u64; + self.0.partial_cmp(&other) + } + } + } +} + +impl_cmp!(usize); +impl_cmp!(u64); + +macro_rules! impl_from { + ($ty:ty) => { + impl From<$ty> for Addr { + fn from(other: $ty) -> Self { + Addr(other as u64) + } + } + } +} + +impl_from!(usize); +impl_from!(u64); diff --git a/src/libvm/src/disassemble.rs b/src/libvm/src/disassemble.rs new file mode 100644 index 0000000..75940a5 --- /dev/null +++ b/src/libvm/src/disassemble.rs @@ -0,0 +1,294 @@ +use crate::{ + error::*, + inst::*, + mem::MemCursor, + reg::*, + visit::*, + vm::{Addr, HalfWord, Word}, +}; +use std::io::Write; + +const WIDTH: usize = 60; + +pub struct Disassemble<'w, 'o> { + writer: &'w mut dyn Write, + cursor: MemCursor<'o>, + addr_offset: Addr, +} + +impl<'w, 'o> Disassemble<'w, 'o> { + pub fn new(writer: &'w mut dyn Write, content: &'o [u8], addr_offset: Addr) -> Self { + Disassemble { + writer, + cursor: MemCursor::new(content), + addr_offset, + } + } + + pub fn is_done(&self) -> bool { + self.cursor.position() >= (self.cursor.get_ref().len() as u64) + } + + fn adv(&mut self) -> Result<()> { + // note the () - this explicitly clones the cursor + let op = self.cursor().next_u16()?; + let next = self.cursor.position() + (inst_len(op) as u64); + self.cursor.set_position(next); + Ok(()) + } + + fn write_addr(&mut self, addr: Addr) { + write!(self.writer, "{:06x} | ", self.addr_offset + addr).unwrap(); + } + + fn write_bytes(&mut self, bytes: &[u8]) { + for b in bytes { + write!(self.writer, "{:02x} ", b).unwrap(); + } + } + + fn write_inst_bytes(&mut self, op: InstOp) { + let len = inst_len(op); + let start = self.cursor.position() as usize; + let end = start + len; + let bytes = &self.cursor.get_ref()[start..end]; + self.write_bytes(bytes); + } + + fn write_r1_r2_inst(&mut self, addr: Addr, op: InstOp, r1: Reg, r2: Reg) { + let len = inst_len(op); + let line_width = 6 + 3 + (3 * len); + let line_offset = WIDTH - line_width; + self.write_addr(addr); + self.write_inst_bytes(op); + + let iname = inst_name(op).unwrap(); + let r1name = reg_name(r1).unwrap().to_lowercase(); + let r2name = reg_name(r2).unwrap().to_lowercase(); + + writeln!( + self.writer, + "{}| {:>10} %{} %{}", + " ".repeat(line_offset), + iname, + r1name, + r2name + ) + .unwrap(); + } + + fn write_r1_inst(&mut self, addr: Addr, op: InstOp, r1: Reg) { + let line_width = 6 + 3 + (3 * inst_len(op)); + let line_offset = WIDTH - line_width; + self.write_addr(addr); + self.write_inst_bytes(op); + + let iname = inst_name(op).unwrap(); + let r1name = reg_name(r1).unwrap().to_lowercase(); + + writeln!( + self.writer, + "{}| {:>10} %{}", + " ".repeat(line_offset), + iname, + r1name, + ) + .unwrap(); + } + + fn write_r1_imm_inst(&mut self, addr: Addr, op: InstOp, r1: Reg, imm: Word) { + let line_width = 6 + 3 + (3 * inst_len(op)); + let line_offset = WIDTH - line_width; + self.write_addr(addr); + self.write_inst_bytes(op); + + let iname = inst_name(op).unwrap(); + let r1name = reg_name(r1).unwrap().to_lowercase(); + + writeln!( + self.writer, + "{}| {:>10} %{} {:#X}", + " ".repeat(line_offset), + iname, + r1name, + imm, + ) + .unwrap(); + } +} + +impl VisitInst for Disassemble<'_, '_> { + type Out = (); + + fn cursor(&self) -> MemCursor { + self.cursor.clone() + } + + fn add(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), ADD, r1, r2); + self.adv()?; + Ok(()) + } + + fn mul(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), MUL, r1, r2); + self.adv()?; + Ok(()) + } + + fn div(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), DIV, r1, r2); + self.adv()?; + Ok(()) + } + + fn mod_(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), MOD, r1, r2); + self.adv()?; + Ok(()) + } + + fn ineg(&mut self, r1: Reg) -> Result { + self.write_r1_inst(self.cursor.position(), INEG, r1); + self.adv()?; + Ok(()) + } + + fn and(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), AND, r1, r2); + self.adv()?; + Ok(()) + } + + fn or(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), OR, r1, r2); + self.adv()?; + Ok(()) + } + + fn inv(&mut self, r1: Reg) -> Result { + self.write_r1_inst(self.cursor.position(), INV, r1); + self.adv()?; + Ok(()) + } + + fn not(&mut self, r1: Reg) -> Result { + self.write_r1_inst(self.cursor.position(), NOT, r1); + self.adv()?; + Ok(()) + } + + fn xor(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), XOR, r1, r2); + self.adv()?; + Ok(()) + } + + fn shl(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), SHL, r1, r2); + self.adv()?; + Ok(()) + } + + fn shr(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), SHR, r1, r2); + self.adv()?; + Ok(()) + } + + fn cmpeq(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), CMPEQ, r1, r2); + self.adv()?; + Ok(()) + } + + fn cmplt(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), CMPLT, r1, r2); + self.adv()?; + Ok(()) + } + + fn jmp(&mut self, r1: Reg) -> Result { + self.write_r1_inst(self.cursor.position(), JMP, r1); + self.adv()?; + Ok(()) + } + + fn jz(&mut self, r1: Reg) -> Result { + self.write_r1_inst(self.cursor.position(), JZ, r1); + self.adv()?; + Ok(()) + } + + fn jnz(&mut self, r1: Reg) -> Result { + self.write_r1_inst(self.cursor.position(), JNZ, r1); + self.adv()?; + Ok(()) + } + + fn load(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), LOAD, r1, r2); + self.adv()?; + Ok(()) + } + + fn regcopy(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), REGCOPY, r1, r2); + self.adv()?; + Ok(()) + } + + fn storeimm64(&mut self, r1: Reg, w1: Word) -> Result { + self.write_r1_imm_inst(self.cursor.position(), STOREIMM64, r1, w1); + self.adv()?; + Ok(()) + } + + fn storeimm32(&mut self, r1: Reg, w1: HalfWord) -> Result { + self.write_r1_imm_inst(self.cursor.position(), STOREIMM32, r1, w1 as u64); + self.adv()?; + Ok(()) + } + + fn memcopy(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), MEMCOPY, r1, r2); + self.adv()?; + Ok(()) + } + + fn store(&mut self, r1: Reg, r2: Reg) -> Result { + self.write_r1_r2_inst(self.cursor.position(), STORE, r1, r2); + self.adv()?; + Ok(()) + } + + fn halt(&mut self) -> Result { + let line_width = 6 + 3 + 3 + 3; + let line_offset = WIDTH - line_width; + self.write_addr(self.cursor.position()); + self.write_inst_bytes(HALT); + writeln!( + self.writer, + "{}| {:>10}", + " ".repeat(line_offset), + "HALT", + ).unwrap(); + self.adv()?; + Ok(()) + } + + fn nop(&mut self) -> Result { + let line_width = 6 + 3 + 3 + 3; + let line_offset = WIDTH - line_width; + self.write_addr(self.cursor.position()); + self.write_inst_bytes(NOP); + writeln!( + self.writer, + "{}| {:>10}", + " ".repeat(line_offset), + "NOP", + ).unwrap(); + self.adv()?; + Ok(()) + } +} diff --git a/src/libvm/src/error.rs b/src/libvm/src/error.rs new file mode 100644 index 0000000..c55f0a3 --- /dev/null +++ b/src/libvm/src/error.rs @@ -0,0 +1,25 @@ +use crate::{inst::InstOp, reg::Reg, addr::*,}; +use snafu::Snafu; + +#[derive(Snafu, Debug, Clone)] +pub enum VmError { + #[snafu(display("illegal register: 0x{:02x}", reg))] + IllegalReg { reg: Reg }, + + #[snafu(display("memory address out of bounds: 0x{:016x}", addr))] + MemOutOfBounds { addr: Addr }, + + #[snafu(display("illegal instruction opcode: 0x{:04x}", op))] + IllegalOp { op: InstOp }, + + #[snafu(display("illegal destination specification: 0b{:08b}", spec))] + IllegalDestSpec { spec: u8 }, + + #[snafu(display("illegal source specification: 0b{:08b}", spec))] + IllegalSourceSpec { spec: u8 }, + + #[snafu(display("object to load spans too much memory"))] + ObjectTooLarge { object_size: usize, max_mem: usize }, +} + +pub type Result = std::result::Result; diff --git a/src/libvm/src/flags.rs b/src/libvm/src/flags.rs new file mode 100644 index 0000000..e35bbfd --- /dev/null +++ b/src/libvm/src/flags.rs @@ -0,0 +1,8 @@ +use bitflags::bitflags; + +bitflags! { + pub struct Flags: u64 { + const HALT = 1; + const COMPARE = 1 << 1; + } +} diff --git a/src/libvm/src/inst.rs b/src/libvm/src/inst.rs new file mode 100644 index 0000000..90f27ad --- /dev/null +++ b/src/libvm/src/inst.rs @@ -0,0 +1,263 @@ +use crate::{addr::Addr, reg::Reg}; + +macro_rules! instructions { + { + $($variant:ident = $value:expr),* $(,)? + } => { + + $( + #[allow(dead_code)] + pub const $variant: InstOp = $value; + )* + + pub fn inst_name(op: InstOp) -> Option<&'static str> { + match op { + $( + $value => Some(stringify!($variant)), + )* + _ => None, + } + } + }; +} +pub type InstOp = u16; + +instructions! { + ADD = 0x0000, + SUB = 0x0001, + MUL = 0x0002, + DIV = 0x0003, + IDIV = 0x0004, + MOD = 0x0005, + AND = 0x0006, + OR = 0x0007, + XOR = 0x0008, + SHL = 0x0009, + SHR = 0x000a, + INEG = 0x000b, + INV = 0x000c, + NOT = 0x000d, + CMPEQ = 0x1000, + CMPLT = 0x1001, + JMP = 0x1002, + JZ = 0x1003, + JNZ = 0x1004, + CALL = 0x2000, + RET = 0x2001, + PUSH = 0x2002, + POP = 0x2003, + MOV = 0x3000, + HALT = 0xF000, + NOP = 0xF001, + DUMP = 0xF002, +} + +#[derive(Debug, Clone, Copy)] +pub enum Inst { + Add(Dest, Source), + Sub(Dest, Source), + Mul(Dest, Source), + Div(Dest, Source), + IDiv(Dest, Source), + Mod(Dest, Source), + And(Dest, Source), + Or(Dest, Source), + Xor(Dest, Source), + Shl(Dest, Source), + Shr(Dest, Source), + INeg(Dest, Source), + Inv(Dest, Source), + Not(Dest, Source), + CmpEq(Source, Source), + CmpLt(Source, Source), + Jmp(Source), + Jz(Source), + Jnz(Source), + Call(Source), + Ret, + Push(Source), + Pop(Dest), + Mov(Dest, Source), + Halt, + Nop, + Dump, +} + +impl Inst { + pub fn op(&self) -> InstOp { + match self { + Inst::Add(_, _) => ADD, + Inst::Sub(_, _) => SUB, + Inst::Mul(_, _) => MUL, + Inst::Div(_, _) => DIV, + Inst::IDiv(_, _) => IDIV, + Inst::Mod(_, _) => MOD, + Inst::And(_, _) => AND, + Inst::Or(_, _) => OR, + Inst::Xor(_, _) => XOR, + Inst::Shl(_, _) => SHL, + Inst::Shr(_, _) => SHL, + Inst::INeg(_, _) => INEG, + Inst::Inv(_, _) => INV, + Inst::Not(_, _) => NOT, + Inst::CmpEq(_, _) => CMPEQ, + Inst::CmpLt(_, _) => CMPLT, + Inst::Jmp(_) => JMP, + Inst::Jz(_) => JZ, + Inst::Jnz(_) => JNZ, + Inst::Mov(_, _) => MOV, + Inst::Call(_) => CALL, + Inst::Ret => RET, + Inst::Push(_) => PUSH, + Inst::Pop(_) => POP, + Inst::Halt => HALT, + Inst::Nop => NOP, + Inst::Dump => DUMP, + } + } + + pub fn len(&self) -> usize { + match self { + Inst::Add(dest, source) + | Inst::Sub(dest, source) + | Inst::Mul(dest, source) + | Inst::Div(dest, source) + | Inst::IDiv(dest, source) + | Inst::Mod(dest, source) + | Inst::And(dest, source) + | Inst::Or(dest, source) + | Inst::Xor(dest, source) + | Inst::Shl(dest, source) + | Inst::Shr(dest, source) + | Inst::INeg(dest, source) + | Inst::Inv(dest, source) + | Inst::Not(dest, source) + | Inst::Mov(dest, source) => { 3 + dest.len() + source.len() } + Inst::CmpEq(s1, s2) + | Inst::CmpLt(s1, s2) => { 3 + s1.len() + s2.len() } + Inst::Jmp(v) + | Inst::Jz(v) + | Inst::Jnz(v) + | Inst::Call(v) + | Inst::Push(v) => { 3 + v.len() } + Inst::Pop(v) => { 3 + v.len() } + Inst::Ret + | Inst::Halt + | Inst::Nop + | Inst::Dump => { 2 } + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Source { + Addr64(Addr), + Addr32(Addr), + Addr16(Addr), + Addr8(Addr), + RegAddr64(Reg), + RegAddr32(Reg), + RegAddr16(Reg), + RegAddr8(Reg), + Reg(Reg), + Imm(u64), +} + +impl Source { + /// The length of this source in its instruction. + pub fn len(&self) -> usize { + match self { + Source::Addr64(_) | Source::Addr32(_) | Source::Addr16(_) | Source::Addr8(_) => 8, + Source::RegAddr64(_) | Source::RegAddr32(_) | Source::RegAddr16(_) | Source::RegAddr8(_) => 1, + Source::Reg(_) => 1, + Source::Imm(_) => 8, + } + } + + /// The length of the value that this source points to. + pub fn value_len(&self) -> usize { + match self { + Source::Addr64(_) | Source::RegAddr64(_) | Source::Reg(_) | Source::Imm(_) => 8, + Source::Addr32(_) | Source::RegAddr32(_) => 4, + Source::Addr16(_) | Source::RegAddr16(_) => 2, + Source::Addr8(_) | Source::RegAddr8(_) => 1, + } + } +} + +impl From for Source { + fn from(other: Dest) -> Self { + match other { + Dest::Addr64(a) => Source::Addr64(a), + Dest::Addr32(a) => Source::Addr32(a), + Dest::Addr16(a) => Source::Addr16(a), + Dest::Addr8(a) => Source::Addr8(a), + Dest::RegAddr64(r) => Source::RegAddr64(r), + Dest::RegAddr32(r) => Source::RegAddr32(r), + Dest::RegAddr16(r) => Source::RegAddr16(r), + Dest::RegAddr8(r) => Source::RegAddr8(r), + Dest::Reg(r) => Source::Reg(r), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Dest { + Addr64(Addr), + Addr32(Addr), + Addr16(Addr), + Addr8(Addr), + RegAddr64(Reg), + RegAddr32(Reg), + RegAddr16(Reg), + RegAddr8(Reg), + Reg(Reg), +} + +impl Dest { + /// The length of this destination in its instruction. + pub fn len(&self) -> usize { + match self { + Dest::Addr64(_) | Dest::Addr32(_) | Dest::Addr16(_) | Dest::Addr8(_) => 8, + Dest::RegAddr64(_) | Dest::RegAddr32(_) | Dest::RegAddr16(_) | Dest::RegAddr8(_) => 1, + Dest::Reg(_) => 1, + } + } + + /// The length of the value that this destination points to. + pub fn value_len(&self) -> usize { + match self { + Dest::Addr64(_) | Dest::RegAddr64(_) | Dest::Reg(_) => 8, + Dest::Addr32(_) | Dest::RegAddr32(_) => 4, + Dest::Addr16(_) | Dest::RegAddr16(_) => 2, + Dest::Addr8(_) | Dest::RegAddr8(_) => 1, + } + } +} + +// TODO : make this an enum + +pub const DEST_ADDR64: u8 = 0b0000; +pub const DEST_ADDR32: u8 = 0b0001; +pub const DEST_ADDR16: u8 = 0b0010; +pub const DEST_ADDR8: u8 = 0b0011; +pub const DEST_REG_ADDR64: u8 = 0b0100; +pub const DEST_REG_ADDR32: u8 = 0b0101; +pub const DEST_REG_ADDR16: u8 = 0b0110; +pub const DEST_REG_ADDR8: u8 = 0b0111; +/* immediates - not used, invalid */ +pub const DEST_REG: u8 = 0b1100; + +pub const SOURCE_ADDR64: u8 = 0b0000; +pub const SOURCE_ADDR32: u8 = 0b0001; +pub const SOURCE_ADDR16: u8 = 0b0010; +pub const SOURCE_ADDR8: u8 = 0b0011; +pub const SOURCE_REG_ADDR64: u8 = 0b0100; +pub const SOURCE_REG_ADDR32: u8 = 0b0101; +pub const SOURCE_REG_ADDR16: u8 = 0b0110; +pub const SOURCE_REG_ADDR8: u8 = 0b0111; +pub const SOURCE_IMM64: u8 = 0b1000; +pub const SOURCE_IMM32: u8 = 0b1001; +pub const SOURCE_IMM16: u8 = 0b1010; +pub const SOURCE_IMM8: u8 = 0b1011; +pub const SOURCE_REG: u8 = 0b1100; diff --git a/src/libvm/src/lib.rs b/src/libvm/src/lib.rs new file mode 100644 index 0000000..dc4270d --- /dev/null +++ b/src/libvm/src/lib.rs @@ -0,0 +1,8 @@ +pub mod addr; +pub mod error; +pub mod flags; +pub mod inst; +pub mod mem; +pub mod obj; +pub mod reg; +pub mod state; diff --git a/src/libvm/src/mem.rs b/src/libvm/src/mem.rs new file mode 100644 index 0000000..2a0e15f --- /dev/null +++ b/src/libvm/src/mem.rs @@ -0,0 +1,278 @@ +use crate::{addr::*, error::*, inst::*, reg::*}; +use byteorder::{ReadBytesExt, WriteBytesExt, LE}; +use std::io::Cursor; + +pub struct MemCursor { + cursor: Cursor, +} + +impl MemCursor + where Cursor: ReadBytesExt, + T: AsRef<[u8]> +{ + pub fn new(mem: T) -> Self { + MemCursor { cursor: Cursor::new(mem) } + } + + pub fn position(&self) -> u64 { + self.cursor.position() + } + + pub fn set_position>(&mut self, position: P) { + self.cursor.set_position((position.into()).0) + } + + pub fn next_u8_unchecked(&mut self) -> u8 { + self.cursor.read_u8().unwrap() + } + + pub fn next_u8(&mut self) -> Result { + self.check_addr(self.position()) + .map(|_| self.next_u8_unchecked()) + } + + pub fn next_u16_unchecked(&mut self) -> u16 { + self.cursor.read_u16::().unwrap() + } + + pub fn next_u16(&mut self) -> Result { + self.check_addr(self.position()) + .map(|_| self.next_u16_unchecked()) + } + + pub fn next_u32_unchecked(&mut self) -> u32 { + self.cursor.read_u32::().unwrap() + } + + pub fn next_u32(&mut self) -> Result { + self.check_addr(self.position()) + .map(|_| self.next_u32_unchecked()) + } + + pub fn next_u64_unchecked(&mut self) -> u64 { + self.cursor.read_u64::().unwrap() + } + + pub fn next_u64(&mut self) -> Result { + self.check_addr(self.position()) + .map(|_| self.next_u64_unchecked()) + } + + pub fn next_addr(&mut self) -> Result { + self.check_addr(self.position()) + .map(|_| self.next_addr_unchecked()) + } + + pub fn next_addr_unchecked(&mut self) -> Addr { + Addr(self.next_u64_unchecked()) + } + + pub fn next_inst(&mut self) -> Result { + let start = self.position(); + let op = self.next_u16()?; + + macro_rules! dest_source { + ($variant:ident) => {{ + let (d, s) = self.next_dest_source()?; + Ok(Inst::$variant(d, s)) + }}; + } + macro_rules! source_source { + ($variant:ident) => {{ + let (s1, s2) = self.next_source_source()?; + Ok(Inst::$variant(s1, s2)) + }}; + } + macro_rules! source { + ($variant:ident) => {{ + let spec = (self.next_u8()? & 0xF0) >> 4; + let source = self.next_source(spec)?; + Ok(Inst::$variant(source)) + }}; + } + macro_rules! dest { + ($variant:ident) => {{ + let spec = (self.next_u8()? & 0xF0) >> 4; + let dest = self.next_dest(spec)?; + Ok(Inst::$variant(dest)) + }}; + } + let inst = match op { + ADD => dest_source!(Add), + SUB => dest_source!(Sub), + MUL => dest_source!(Mul), + DIV => dest_source!(Div), + IDIV => dest_source!(IDiv), + MOD => dest_source!(Mod), + AND => dest_source!(And), + OR => dest_source!(Or), + XOR => dest_source!(Xor), + SHL => dest_source!(Shl), + SHR => dest_source!(Shr), + INEG => dest_source!(INeg), + INV => dest_source!(Inv), + NOT => dest_source!(Not), + CMPEQ => source_source!(CmpEq), + CMPLT => source_source!(CmpLt), + JMP => source!(Jmp), + JZ => source!(Jz), + JNZ => source!(Jnz), + CALL => source!(Call), + RET => Ok(Inst::Ret), + PUSH => source!(Push), + POP => dest!(Pop), + MOV => dest_source!(Mov), + HALT => Ok(Inst::Halt), + NOP => Ok(Inst::Nop), + DUMP => Ok(Inst::Dump), + _ => Err(VmError::IllegalOp { op }), + }?; + let end = self.position(); + let len = (end - start) as usize; + assert_eq!(len, inst.len()); + Ok(inst) + } + + fn next_source_source(&mut self) -> Result<(Source, Source)> { + let spec = self.next_u8()?; + let s1_spec = (spec & 0xF0) >> 4; + let s2_spec = spec & 0x0F; + let s1 = self.next_source(s1_spec)?; + let s2 = self.next_source(s2_spec)?; + Ok((s1, s2)) + } + + fn next_dest_source(&mut self) -> Result<(Dest, Source)> { + let spec = self.next_u8()?; + let dest_spec = (spec & 0xF0) >> 4; + let source_spec = spec & 0x0F; + let dest = self.next_dest(dest_spec)?; + let source = self.next_source(source_spec)?; + Ok((dest, source)) + } + + fn next_dest(&mut self, spec: u8) -> Result { + match spec { + DEST_ADDR64 => Ok(Dest::Addr64(self.next_addr()?)), + DEST_ADDR32 => Ok(Dest::Addr32(self.next_addr()?)), + DEST_ADDR16 => Ok(Dest::Addr16(self.next_addr()?)), + DEST_ADDR8 => Ok(Dest::Addr8(self.next_addr()?)), + DEST_REG_ADDR64 => Ok(Dest::RegAddr64(self.next_reg()?)), + DEST_REG_ADDR32 => Ok(Dest::RegAddr32(self.next_reg()?)), + DEST_REG_ADDR16 => Ok(Dest::RegAddr16(self.next_reg()?)), + DEST_REG_ADDR8 => Ok(Dest::RegAddr8(self.next_reg()?)), + DEST_REG => Ok(Dest::Reg(self.next_reg()?)), + _ => Err(VmError::IllegalDestSpec { spec }), + } + } + + fn next_source(&mut self, spec: u8) -> Result { + match spec { + SOURCE_ADDR64 => Ok(Source::Addr64(self.next_addr()?)), + SOURCE_ADDR32 => Ok(Source::Addr32(self.next_addr()?)), + SOURCE_ADDR16 => Ok(Source::Addr16(self.next_addr()?)), + SOURCE_ADDR8 => Ok(Source::Addr8(self.next_addr()?)), + SOURCE_REG_ADDR64 => Ok(Source::RegAddr64(self.next_reg()?)), + SOURCE_REG_ADDR32 => Ok(Source::RegAddr32(self.next_reg()?)), + SOURCE_REG_ADDR16 => Ok(Source::RegAddr16(self.next_reg()?)), + SOURCE_REG_ADDR8 => Ok(Source::RegAddr8(self.next_reg()?)), + SOURCE_REG => Ok(Source::Reg(self.next_reg()?)), + SOURCE_IMM64 => Ok(Source::Imm(self.next_u64()?)), + SOURCE_IMM32 => Ok(Source::Imm(self.next_u32()? as u64)), + SOURCE_IMM16 => Ok(Source::Imm(self.next_u16()? as u64)), + SOURCE_IMM8 => Ok(Source::Imm(self.next_u8()? as u64)), + _ => Err(VmError::IllegalSourceSpec { spec }), + } + } + + fn next_reg(&mut self) -> Result { + let reg = self.next_u8()?; + if (reg as usize) >= NUM_REGS { + Err(VmError::IllegalReg { reg }) + } else { + Ok(reg) + } + } +} + +impl MemCursor + where T: AsRef<[u8]> +{ + fn check_addr(&self, addr: u64) -> Result<()> { + if addr > (self.cursor.get_ref().as_ref().len() as u64) { + Err(VmError::MemOutOfBounds { addr: Addr(addr) }) + } else { + Ok(()) + } + } +} + +impl MemCursor + where Cursor: WriteBytesExt, + T: AsRef<[u8]> +{ + pub fn write_u8_unchecked(&mut self, value: u8) { + self.cursor.write_u8(value).unwrap(); + } + + pub fn write_u8(&mut self, value: u8) -> Result<()> { + self.check_addr(self.position()) + .map(|_| self.write_u8_unchecked(value)) + } + + pub fn write_u16_unchecked(&mut self, value: u16) { + self.cursor.write_u16::(value).unwrap(); + } + + pub fn write_u16(&mut self, value: u16) -> Result<()> { + self.check_addr(self.position()) + .map(|_| self.write_u16_unchecked(value)) + } + + pub fn write_u32_unchecked(&mut self, value: u32) { + self.cursor.write_u32::(value).unwrap(); + } + + pub fn write_u32(&mut self, value: u32) -> Result<()> { + self.check_addr(self.position()) + .map(|_| self.write_u32_unchecked(value)) + } + + pub fn write_u64_unchecked(&mut self, value: u64) { + self.cursor.write_u64::(value).unwrap(); + } + + pub fn write_u64(&mut self, value: u64) -> Result<()> { + self.check_addr(self.position()) + .map(|_| self.write_u64_unchecked(value)) + } +} + +/* +//////////////////////////////////////////////////////////////////////////////// +// Index impl +//////////////////////////////////////////////////////////////////////////////// +impl> Index for MemCursor { + type Output = u8; + + fn index(&self, addr: usize) -> &Self::Output { + self.mem.as_ref().index(addr) + } +} + +impl> Index for MemCursor { + type Output = u8; + + fn index(&self, addr: u64) -> &Self::Output { + self.index(addr as usize) + } +} + +impl> Index for MemCursor { + type Output = u8; + + fn index(&self, addr: Addr) -> &Self::Output { + self.index(addr.0) + } +} +*/ diff --git a/src/libvm/src/obj/assemble.rs b/src/libvm/src/obj/assemble.rs new file mode 100644 index 0000000..7d631f0 --- /dev/null +++ b/src/libvm/src/obj/assemble.rs @@ -0,0 +1,360 @@ +pub mod error; +pub mod session; +mod includes; +mod names; + +use self::{error::*, session::AsmSession}; +use crate::{ + addr::Addr, + inst, + obj::{ + obj::{self, Object}, + syn::ast::*, + } +}; +use byteorder::{WriteBytesExt, LE}; +use std::{collections::HashMap, path::Path}; + +pub trait Asm { + type Out; + fn assemble(&self, asm: &mut AsmSession) -> Result; +} + +pub fn assemble_path(path: impl AsRef) -> Result { + let mut session = AsmSession::default(); + session.include_path(path)?; + session.assemble() +} + +impl Asm for Vec<&'_ Directive> { + type Out = obj::Object; + fn assemble(&self, asm: &mut AsmSession) -> Result { + let sections = self + .iter() + .filter_map(|section| section.assemble(asm).transpose()) + .collect::>()?; + Ok(obj::Object { + version: obj::OBJ_VERSION, + sections, + }) + } +} + +impl Asm for Directive { + type Out = Option; + + fn assemble(&self, asm: &mut AsmSession) -> Result { + match self { + Directive::Data(section) => Ok(Some(obj::Section::Data(section.assemble(asm)?))), + Directive::Meta(section) => section.assemble(asm).map(Some), + Directive::Include(_) => { Ok(None) } + } + } +} + +impl Asm for DataSection { + type Out = obj::DataSection; + + fn assemble(&self, session: &mut AsmSession) -> Result { + let names = names::get_section_names(self)?; + session.name_stack.push(names); + let section_len = self.len() as u64; + let (start, end) = match self.org { + SectionOrg::Start(start) => (start, start + (section_len as u64)), + SectionOrg::StartEnd(start, end) => (start, end), + }; + session.pos = Addr(start); + if start > end { + return Err(AsmError::StartGreaterThanEnd { start, end }); + } + let len = end - start - 1; + if len > section_len { + return Err(AsmError::SectionTooShort { + section_end: end, + section_size: start + section_len, + }); + } + + let mut contents = Vec::with_capacity(section_len as usize); + for line in self.lines.iter() { + contents.extend(line.assemble(session)?); + session.pos += line.len(); + } + assert_eq!( + contents.len() as u64, + section_len, + "in section {}", + self.name + ); + session.name_stack.pop(); + Ok(obj::DataSection { + name: self.name.clone(), + start, + len: section_len, + contents, + }) + } +} + +impl Asm for MetaSection { + type Out = obj::Section; + + fn assemble(&self, session: &mut AsmSession) -> Result { + let mut entries = HashMap::new(); + for line in self.lines.iter() { + if entries.contains_key(&line.name) { + return Err(AsmError::DuplicateMetaName { + name: line.name.to_string(), + }); + } + let value = match &line.value { + Value::Int(i) => *i, + Value::Name(s) => session.lookup_name(s.as_str()) + .ok_or_else(|| AsmError::UnknownName { name: s.to_string() })?.addr.0, + Value::Reg(_) | Value::Here | Value::Addr(_, _) => { + return Err(AsmError::IllegalMetaValue { + name: line.name.to_string(), + value: line.value.clone(), + }) + } // TODO : + // * deref constexpr? + // * pre-startup static init? + }; + entries.insert(line.name.to_string(), value); + } + Ok(obj::Section::Meta(obj::MetaSection { entries })) + } +} + +impl Asm for DataLine { + type Out = Vec; + + fn assemble(&self, session: &mut AsmSession) -> Result { + match self { + DataLine::ValueDef(v) => v.assemble(session), + DataLine::Inst(i) => i.assemble(session), + DataLine::Export(_) | DataLine::Label(_) => Ok(Vec::new()), + } + } +} + +impl Asm for ValueDef { + type Out = Vec; + + fn assemble(&self, _: &mut AsmSession) -> Result { + match self { + ValueDef::Int(x, s) => Ok(x.to_le_bytes().iter().copied().take(s.len()).collect()), + ValueDef::String(s) => { + let bytes = s.bytes(); + let mut out = s.len().to_le_bytes().to_vec(); + out.extend(bytes); + Ok(out) + } + ValueDef::ZString(z) => { + let bytes = z.bytes(); + let mut out = z.len().to_le_bytes().to_vec(); + out.extend(bytes); + Ok(out) + } + } + } +} + +impl Asm for Inst { + type Out = Vec; + + fn assemble(&self, session: &mut AsmSession) -> Result { + let len = self.len(); + + macro_rules! map_inst { + ($op:expr, $dest:expr, $source:expr) => {{ + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::($op).unwrap(); + let dest = $dest; + let dest_encoding = + dest.dest_encoding() + .ok_or_else(|| AsmError::IllegalDestValue { + value: dest.clone(), + })?; + let source = $source; + let source_encoding = source.source_encoding(); + bytes + .write_u8((dest_encoding << 4) | source_encoding) + .unwrap(); + bytes.extend(dest.assemble(session)?); + bytes.extend(source.assemble(session)?); + assert_eq!( + self.len(), + bytes.len(), + "instruction size mismatch in {} instruction - {:?} produces these bytes {:?}", + stringify!($op), + self, + bytes + ); + Ok(bytes) + }}; + + ($op:expr, $source:expr) => {{ + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::($op).unwrap(); + let source = $source; + let source_encoding = source.source_encoding() << 4; + bytes.write_u8(source_encoding).unwrap(); + bytes.extend(source.assemble(session)?); + assert_eq!( + self.len(), + bytes.len(), + "instruction size mismatch in {} instruction - {:?} produces these bytes {:?}", + stringify!($op), + self, + bytes + ); + Ok(bytes) + }}; + + ($op:expr) => {{ + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::($op).unwrap(); + assert_eq!( + self.len(), + bytes.len(), + "instruction size mismatch in {} instruction - {:?} produces these bytes {:?}", + stringify!($op), + self, + bytes + ); + Ok(bytes) + }}; + } + match self { + Inst::Add(v1, v2) => map_inst!(inst::ADD, v1, v2), + Inst::Sub(v1, v2) => map_inst!(inst::SUB, v1, v2), + Inst::Mul(v1, v2) => map_inst!(inst::MUL, v1, v2), + Inst::Div(v1, v2) => map_inst!(inst::DIV, v1, v2), + Inst::IDiv(v1, v2) => map_inst!(inst::IDIV, v1, v2), + Inst::Mod(v1, v2) => map_inst!(inst::MOD, v1, v2), + Inst::And(v1, v2) => map_inst!(inst::AND, v1, v2), + Inst::Or(v1, v2) => map_inst!(inst::OR, v1, v2), + Inst::Xor(v1, v2) => map_inst!(inst::XOR, v1, v2), + Inst::Shl(v1, v2) => map_inst!(inst::SHL, v1, v2), + Inst::Shr(v1, v2) => map_inst!(inst::SHR, v1, v2), + Inst::INeg(v1, v2) => map_inst!(inst::INEG, v1, v2), + Inst::Inv(v1, v2) => map_inst!(inst::INV, v1, v2), + Inst::Not(v1, v2) => map_inst!(inst::NOT, v1, v2), + // TODO/BUG: CmpEq and CmpLt both take two sources instead of a source and destination + Inst::CmpEq(v1, v2) => map_inst!(inst::CMPEQ, v1, v2), + Inst::CmpLt(v1, v2) => map_inst!(inst::CMPLT, v1, v2), + Inst::Jmp(v) => map_inst!(inst::JMP, v), + Inst::Jz(v) => map_inst!(inst::JZ, v), + Inst::Jnz(v) => map_inst!(inst::JNZ, v), + Inst::Call(v) => map_inst!(inst::CALL, v), + Inst::Ret => map_inst!(inst::RET), + Inst::Push(v) => map_inst!(inst::PUSH, v), + Inst::Pop(dest) => { + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::(inst::POP).unwrap(); + let dest_encoding = dest.source_encoding() << 4; + bytes.write_u8(dest_encoding).unwrap(); + bytes.extend(dest.assemble(session)?); + assert_eq!( + self.len(), + bytes.len(), + "instruction size mismatch in inst::PUSH instruction - {:?} produces these bytes {:?}", + self, + bytes + ); + Ok(bytes) + } + Inst::Mov(v1, v2) => map_inst!(inst::MOV, v1, v2), + Inst::Halt => map_inst!(inst::HALT), + Inst::Nop => map_inst!(inst::NOP), + Inst::Dump => map_inst!(inst::DUMP), + } + } +} + +impl Asm for Value { + type Out = Vec; + + fn assemble(&self, session: &mut AsmSession) -> Result { + match self { + Value::Int(i) => Ok(i.to_le_bytes().to_vec()), + Value::Reg(r) => Ok(vec![*r]), + Value::Name(name) => { + let value = session.lookup_name(name.as_str()) + .ok_or_else(|| AsmError::UnknownName { name: name.to_string() })?; + Ok(value.addr.0.to_le_bytes().to_vec()) + } + Value::Here => Ok(session.pos.0.to_le_bytes().to_vec()), + Value::Addr(v, _) => { + if let Value::Addr(_, _) = &**v { + // double deref is not allowed + todo!() + } else { + v.assemble(session) + } + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_inst_len() { + let mut session = AsmSession::default(); + //asm.names + //.push(vec![("test".to_string(), Addr(0u64))].into_iter().collect()); + + macro_rules! assert_len { + ($inst:expr) => {{ + let inst = $inst; + let asm_size = $inst.assemble(&mut session).unwrap().len(); + assert_eq!(inst.len(), asm_size, "Instruction {:?}.len() indicates it should be {} bytes long but was assembled as {} bytes", inst, inst.len(), asm_size); + }} + } + + use Inst::*; + + let dummy_dests = &[ + Value::Reg(0), + Value::Addr(Box::new(Value::Reg(0)), IntSize::U8), + Value::Addr(Box::new(Value::Here), IntSize::U16), + //Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32), + Value::Addr(Box::new(Value::Int(0)), IntSize::U64), + ]; + + let dummy_sources = &[ + Value::Int(0), + Value::Reg(0), + //Value::Name("test".to_string()), + Value::Here, + Value::Addr(Box::new(Value::Reg(0)), IntSize::U8), + Value::Addr(Box::new(Value::Here), IntSize::U16), + //Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32), + Value::Addr(Box::new(Value::Int(0)), IntSize::U32), + ]; + + for v1 in dummy_dests { + for v2 in dummy_sources { + assert_len!(Add(v1.clone(), v2.clone())); + assert_len!(Sub(v1.clone(), v2.clone())); + assert_len!(Mul(v1.clone(), v2.clone())); + assert_len!(Div(v1.clone(), v2.clone())); + assert_len!(Mod(v1.clone(), v2.clone())); + assert_len!(And(v1.clone(), v2.clone())); + assert_len!(Or(v1.clone(), v2.clone())); + assert_len!(Xor(v1.clone(), v2.clone())); + assert_len!(Shl(v1.clone(), v2.clone())); + assert_len!(Shr(v1.clone(), v2.clone())); + assert_len!(INeg(v1.clone(), v2.clone())); + assert_len!(Inv(v1.clone(), v2.clone())); + assert_len!(Not(v1.clone(), v2.clone())); + assert_len!(Mov(v1.clone(), v2.clone())); + // TODO more length tests + } + } + } +} diff --git a/src/libvm/src/obj/assemble/error.rs b/src/libvm/src/obj/assemble/error.rs new file mode 100644 index 0000000..a1f0e03 --- /dev/null +++ b/src/libvm/src/obj/assemble/error.rs @@ -0,0 +1,114 @@ +use crate::obj::syn::ast::*; +use snafu::Snafu; +use std::path::PathBuf; + +pub type LexError = lrpar::LexError; +pub type ParseError = lrpar::ParseError; +pub type LexParseError = lrpar::LexParseError; + +#[derive(Debug, Snafu)] +pub enum AsmError { + #[snafu(display("unknown name: {}", name))] + UnknownName { + name: String, + }, + + #[snafu(display("unknown export name: {}", name))] + UnknownExport { + name: String, + }, + + #[snafu(display("duplicate label definition: {}", name))] + DuplicateLabel { + name: String, + }, + + #[snafu(display("duplicate meta entry name: {}", name))] + DuplicateMetaName { + name: String, + }, + + #[snafu(display("illegal meta value for entry name {}: {:?}", name, value))] + IllegalMetaValue { + name: String, + value: Value, + }, + + #[snafu(display("duplicate exported name: {}", name))] + DuplicateExport { + name: String, + }, + + #[snafu(display("section start ({:#x}) is greater than end ({:#x})", start, end))] + StartGreaterThanEnd { + start: u64, + end: u64, + }, + + #[snafu(display( + "section end ({:#x}) too short for section content size ({:#x})", + section_end, + section_size + ))] + SectionTooShort { + section_end: u64, + section_size: u64, + }, + + #[snafu(display("illegal instruction destination value: {:?}", value))] + IllegalDestValue { + value: Value, + }, + + #[snafu(display("deref of a deref value is not allowed"))] + DoubleDeref { + value: Value, + }, + + // TODO(asm) : Path error wrapper for assembling things + #[snafu(display("could not read path: {}", path.display()))] + BadPath { + path: PathBuf, + }, + + Syntax { + source: SyntaxError, + }, +} + +#[derive(Debug, Snafu)] +pub enum SyntaxError { + Lex { source: LexError }, + Parse { source: ParseError }, + Multi { errors: Vec }, +} + +impl From for SyntaxError { + fn from(source: LexError) -> Self { + SyntaxError::Lex { source } + } +} + +impl From for SyntaxError { + fn from(source: ParseError) -> Self { + SyntaxError::Parse { source } + } +} + +impl From> for SyntaxError { + fn from(errors: Vec) -> Self { + SyntaxError::Multi { errors } + } +} + +impl From for SyntaxError { + fn from(source: LexParseError) -> Self { + match source { + LexParseError::LexError(e) => SyntaxError::Lex { source: e }, + LexParseError::ParseError(e) => SyntaxError::Parse { source: e }, + } + } +} + +pub type Result = std::result::Result; + diff --git a/src/libvm/src/obj/assemble/includes.rs b/src/libvm/src/obj/assemble/includes.rs new file mode 100644 index 0000000..ca402fe --- /dev/null +++ b/src/libvm/src/obj/assemble/includes.rs @@ -0,0 +1,68 @@ +use crate::obj::{ + assemble::{error::*, session::AsmSession, Asm}, + syn::{ast::Directive, lexer, parser}, +}; +use std::{path::PathBuf, rc::Rc}; + +#[derive(Debug, Clone)] +pub struct GetIncludes { + path: PathBuf, +} + +impl GetIncludes { + pub fn assemble_from_path(path: PathBuf, session: &mut AsmSession) -> Result<()> { + let path = path + .canonicalize() + .map_err(|_| AsmError::BadPath { path })?; + GetIncludes { path }.assemble(session) + } +} + +impl Asm for GetIncludes { + type Out = (); + + fn assemble(&self, session: &mut AsmSession) -> Result { + assert!(self.path.is_absolute()); + if session.includes.contains_key(&self.path) { + return Ok(()); + } + + let text = std::fs::read_to_string(&self.path).map_err(|_| AsmError::BadPath { + path: self.path.clone(), + })?; + + let lexerdef = lexer::lexerdef(); + let lexer = lexerdef.lexer(&text); + let (res, errors) = parser::parse(&lexer); + + if !errors.is_empty() { + return Err(AsmError::Syntax { + source: errors.into(), + }); + } + + // insert a dummy AST in the includes and replace it when we're done with the actual AST + Rc::get_mut(&mut session.includes) + .unwrap() + .insert(self.path.clone(), Default::default()); + session.include_stack.push(self.path.clone()); + let ast = res.unwrap(); + for directive in ast.iter() { + if let Directive::Include(include_path) = directive { + GetIncludes::assemble_from_path(PathBuf::from(include_path), session)?; + } + } + session.include_stack.pop(); + + let dummy = Rc::get_mut(&mut session.includes) + .unwrap() + .insert(self.path.clone(), ast) + .expect("ast was removed after its inclusion (why did you do that?)"); + assert!( + dummy.is_empty(), + "ast was modified after its inclusion (why did you do that?)" + ); + + Ok(()) + } +} diff --git a/src/libvm/src/obj/assemble/names.rs b/src/libvm/src/obj/assemble/names.rs new file mode 100644 index 0000000..c7a30fd --- /dev/null +++ b/src/libvm/src/obj/assemble/names.rs @@ -0,0 +1,104 @@ +use crate::{ + addr::Addr, + obj::{ + assemble::{ + session::AsmSession, + error::*, + }, + syn::ast::{DataSection, DataLine, Directive}, + }, +}; +use std::{collections::{HashMap, HashSet}, rc::Rc}; + +// TODO(asm) make custom Names type that has "merge" that will catch errors with duplicate names +pub type Names = HashMap; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Name { + pub name: String, + pub addr: Addr, + pub export: bool, +} + +pub fn get_section_names(section: &DataSection) -> Result { + let mut pos = Addr(section.org.start()); + let mut names = HashMap::new(); + let mut exports = HashSet::new(); + + // This isn't immediately straightforward in code, so this is what's happening. + // + // A name may be specified exactly once. It is added to the "names" mapping. If the name is + // specified again, it is a duplicate error and handled as such. A name may be flagged as + // being exported. + // + // Exports are gathered at the beginning into a set. Afterwards, as name definitions + // are gathered, exports are removed from their set if they exist. Since duplicate names + // cause an error, export names will only be removed once. + // + // At the end of name gathering, if any exports have not been removed, then we know those + // are exports whose names are undefined and we can return an UnknownExport error. + + // get exported names + for line in section.lines.iter() { + if let DataLine::Export(name) = line { + exports.insert(name); + } + } + + // get names + for line in section.lines.iter() { + if let DataLine::Label(name) = line { + if names.contains_key(name) { + return Err(AsmError::DuplicateLabel { name: name.clone() }); + } + let export = exports.remove(name); + + names.insert(name.clone(), Name { + name: name.clone(), + addr: pos, + export, + }); + } + pos += line.len(); + } + + // all exports map 1:1 with names + if exports.is_empty() { + Ok(names) + } else { + Err(AsmError::UnknownExport { name: exports.iter().next().unwrap().to_string() }) + } +} + +pub fn get_exports(session: &mut AsmSession) -> Result { + let includes = Rc::clone(&session.includes); + let mut all_exports = Vec::new(); + + // get *all* exports first + for (_, ast) in includes.iter() { + for directive in ast.iter() { + if let Directive::Data(section) = directive { + let names: HashMap<_, _> = get_section_names(section)? + .into_iter() + .filter(|(_, name)| name.export) + .collect(); + all_exports.push(names); + } + } + } + + let mut exports = HashSet::new(); + + // make a set of all export names, erroring on duplicate export + for name_map in all_exports.iter() { + let names_set: HashSet<_> = name_map.keys().collect(); + if let Some(dupe) = exports.intersection(&names_set).next() { + return Err(AsmError::DuplicateExport { name: dupe.to_string() }); + } + exports.extend(names_set); + } + + // NOTE: this can probably be done with a fancy combinator chain + Ok(all_exports.into_iter() + .fold(Names::new(), |mut acc, val| { acc.extend(val); acc })) +} diff --git a/src/libvm/src/obj/assemble/session.rs b/src/libvm/src/obj/assemble/session.rs new file mode 100644 index 0000000..5ab353c --- /dev/null +++ b/src/libvm/src/obj/assemble/session.rs @@ -0,0 +1,72 @@ +use crate::{ + addr::Addr, + obj::{ + assemble::{ + Asm, + includes::GetIncludes, + names::{self, Name, Names}, + error::*, + }, + obj::Object, + syn::ast::Ast, + }, +}; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, + rc::Rc, +}; + +/// A shared session for the assembler. +#[derive(Debug, Default)] +pub struct AsmSession { + pub (in super) includes: Rc>, + pub (in super) include_search_paths: Vec, + pub (in super) include_stack: Vec, + pub (in super) name_stack: Vec, + pub (in super) pos: Addr, +} + +impl AsmSession { + pub fn assemble(&mut self) -> Result { + let includes = Rc::clone(&self.includes); + let sections: Vec<_> = includes.iter() + .flat_map(|(_, ast)| ast) + .collect(); + sections.assemble(self) + } + + pub fn include_path(&mut self, path: impl AsRef) -> Result<()> { + self.include_stack.clear(); + let path = path.as_ref().to_path_buf(); + + GetIncludes::assemble_from_path(path, self)?; + let exports = names::get_exports(self)?; + // TODO(asm) - merge exports + self.name_stack.push(exports); + Ok(()) + } + + pub (in super) fn current_include_path(&self) -> Option<&Path> { + self.include_stack.last().map(PathBuf::as_path) + } + + pub (in super) fn resolve_include_path(&self, path: impl AsRef) -> Option { + let path = path.as_ref(); + self.current_include_path() + .and_then(|last_path| last_path.parent()) + .map(|last_dir| last_dir.join(path)) + .or_else(|| self.include_search_paths + .iter() + .filter_map(|include| include.join(path).canonicalize().ok()) + .next()) + } + + pub fn lookup_name(&self, name: &str) -> Option<&Name> { + self.name_stack + .iter() + .rev() + .filter_map(|names| names.get(name)) + .next() + } +} diff --git a/src/libvm/src/obj/error.rs b/src/libvm/src/obj/error.rs new file mode 100644 index 0000000..ed45922 --- /dev/null +++ b/src/libvm/src/obj/error.rs @@ -0,0 +1,45 @@ +use snafu::Snafu; +use std::{fmt::Debug, io}; + +#[derive(Debug, Snafu)] +pub enum ParseError { + #[snafu(display("IO error: {}", source))] + Io { source: io::Error }, + + #[snafu(display("wrong magic number"))] + WrongMagic, + + #[snafu(display("unknown section kind: 0x{:02x}", kind))] + UnknownSectionKind { kind: u8 }, + + #[snafu(display("invalid UTF-8 string: {}", source))] + InvalidUtf8String { source: std::string::FromUtf8Error }, + + #[snafu(display("duplicate symbol name: {}", name))] + DuplicateName { name: String }, + + #[snafu(display("duplicate exported symbol name: {}", name))] + DuplicateExportName { name: String }, +} + +macro_rules! into_parse_error { + ( + $($type:ty : $variant:ident),* $(,)? + ) => { + $( + impl From<$type> for ParseError { + fn from(other: $type) -> Self { + ParseError::$variant { source: other } + } + } + )* + } +} + +into_parse_error! { + io::Error: Io, + std::string::FromUtf8Error: InvalidUtf8String, +} + +pub type Result = std::result::Result; + diff --git a/src/libvm/src/obj/mod.rs b/src/libvm/src/obj/mod.rs new file mode 100644 index 0000000..b7c4760 --- /dev/null +++ b/src/libvm/src/obj/mod.rs @@ -0,0 +1,4 @@ +pub mod assemble; +pub mod error; +pub mod obj; +pub mod syn; diff --git a/src/libvm/src/obj/obj.rs b/src/libvm/src/obj/obj.rs new file mode 100644 index 0000000..2cad6e3 --- /dev/null +++ b/src/libvm/src/obj/obj.rs @@ -0,0 +1,269 @@ +use crate::obj::error::{ParseError, Result}; +use byteorder::{ReadBytesExt, WriteBytesExt, LE}; +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, + fmt::Debug, + io::{Cursor, Read, Write}, +}; + +pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11; +pub const OBJ_VERSION: u32 = 0; +const OBJECT_HEADER_LEN: usize = 16; // 8 + 4 + 4 + +#[derive(Debug, Clone, PartialEq)] +pub struct Object { + pub version: u32, + pub sections: Vec
, +} + +impl Object { + pub fn to_bytes(&self) -> Vec { + let mut cursor = Cursor::new(Vec::new()); + cursor.write_u64::(MAGIC).unwrap(); + cursor.write_u32::(OBJ_VERSION).unwrap(); + cursor.write_u32::(self.sections.len() as u32).unwrap(); + for section in self.sections.iter() { + cursor.write(§ion.to_bytes()).unwrap(); + } + cursor.into_inner() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut cursor = Cursor::new(bytes); + let magic = cursor.read_u64::()?; + if magic != MAGIC { + return Err(ParseError::WrongMagic); + } + let version = cursor.read_u32::()?; + let section_count = cursor.read_u32::()?; + + let mut sections = Vec::new(); + for _ in 0..section_count { + let start = cursor.position() as usize; + let section = Section::from_bytes(&bytes[start..])?; + cursor.set_position((start + section.len()) as u64); + sections.push(section); + } + Ok(Object { version, sections }) + } + + pub fn virtual_len(&self) -> usize { + self.sections + .iter() + .map(|s| match s { + Section::Data(DataSection { start, len, .. }) => (start + len) as usize, + Section::Meta { .. } => 0, + }) + .max() + .unwrap_or(0) + } +} + +macro_rules! section_kind { + ( + pub enum $enum_name:ident { + $($name:ident = $value:expr),* $(,)? + } + ) => { + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u8)] + pub enum $enum_name { + $($name = $value),* + } + + impl TryFrom for SectionKind { + type Error = ParseError; + + fn try_from(other: u8) -> std::result::Result { + match other { + $( + $value => Ok($enum_name::$name), + )* + _ => Err(ParseError::UnknownSectionKind { kind: other }), + } + } + } + + impl From for u8 { + fn from(other: SectionKind) -> Self { + match other { + $( + $enum_name::$name => $value, + )* + } + } + } + }; +} + +section_kind! { + pub enum SectionKind { + Data = 0x00, + Meta = 0xFF, + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Section { + Data(DataSection), + Meta(MetaSection), +} + +impl Section { + pub fn len(&self) -> usize { + match self { + Section::Data(s) => { 1 + 8 + s.len() }, + Section::Meta(s) => { 1 + 8 + s.len() }, + } + } + + pub fn to_bytes(&self) -> Vec { + let mut cursor = Cursor::new(Vec::new()); + let bytes = match self { + Section::Data(s) => { + cursor.write_u8(SectionKind::Data.into()).unwrap(); + s.to_bytes() + } + Section::Meta(s) => { + cursor.write_u8(SectionKind::Meta.into()).unwrap(); + s.to_bytes() + }, + }; + cursor.write_u64::(bytes.len() as u64).unwrap(); + cursor.write(&bytes).unwrap(); + cursor.into_inner() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut cursor = Cursor::new(bytes); + let kind: SectionKind = cursor.read_u8()?.try_into()?; + let len = cursor.read_u64::()?; + let start = cursor.position() as usize; + let end = start + len as usize; + + let bytes = &cursor.get_ref()[start..end]; + match kind { + SectionKind::Data => Ok(Section::Data(DataSection::from_bytes(bytes)?)), + SectionKind::Meta => Ok(Section::Meta(MetaSection::from_bytes(bytes)?)), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct DataSection { + pub name: String, + pub start: u64, + pub len: u64, + pub contents: Vec, +} + +impl DataSection { + pub fn len(&self) -> usize { + 2 + self.name.as_bytes().len() + 8 + 8 + self.contents.len() + } + + pub fn to_bytes(&self) -> Vec { + let mut cursor = Cursor::new(Vec::new()); + assert!(self.name.len() < u16::max_value() as usize); + cursor.write_u16::(self.name.len() as u16).unwrap(); + cursor.write(self.name.as_bytes()).unwrap(); + cursor.write_u64::(self.start).unwrap(); + cursor.write_u64::(self.len).unwrap(); + cursor.write(&self.contents).unwrap(); + cursor.into_inner() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut cursor = Cursor::new(bytes); + + let name_len = cursor.read_u16::()? as usize; + let name_start = cursor.position() as usize; + let name_end = name_start + name_len; + let name_bytes = &bytes[name_start .. name_end]; + let name_string = String::from_utf8(name_bytes.to_vec())?; + cursor.set_position(name_end as u64); + + let start = cursor.read_u64::()?; + let len = cursor.read_u64::()?; + + let contents = &bytes[cursor.position() as usize..]; + Ok(DataSection { + name: name_string, + start, + len, + contents: From::from(contents), + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct MetaSection { + pub entries: HashMap, +} + +impl MetaSection { + pub fn len(&self) -> usize { + 8 + self.entries.iter() + .map(|(k, _)| 8 + k.as_bytes().len() + 8) + .sum::() + } + + pub fn to_bytes(&self) -> Vec { + let mut cursor = Cursor::new(Vec::new()); + cursor.write_u64::(self.entries.len() as u64).unwrap(); + for (k, v) in self.entries.iter() { + let key_len = k.as_bytes().len(); + cursor.write_u64::(key_len as u64).unwrap(); + cursor.write(k.as_bytes()).unwrap(); + cursor.write_u64::(*v).unwrap(); + } + cursor.into_inner() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut cursor = Cursor::new(bytes); + let entry_count = cursor.read_u64::()?; + let mut entries = HashMap::new(); + for _ in 0..entry_count { + // key + let key_len = cursor.read_u64::()?; + let mut key_bytes = vec![0u8; key_len as usize]; + cursor.read_exact(&mut key_bytes)?; + let key = String::from_utf8(key_bytes)?; + + // value + let value = cursor.read_u64::()?; + entries.insert(key, value); + } + Ok(MetaSection { entries }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_to_from_bytes() { + let obj = Object { + version: OBJ_VERSION, + sections: vec![ + Section::Data(DataSection { + name: "data".to_string(), + start: 0, + len: 16, + contents: vec!(0u8; 16), + }), + Section::Meta(MetaSection { + entries: Default::default(), + }) + ], + }; + + let obj_bytes = obj.to_bytes(); + let converted = Object::from_bytes(&obj_bytes).unwrap(); + assert_eq!(obj, converted); + } +} diff --git a/src/libvm/src/obj/syn/ast.rs b/src/libvm/src/obj/syn/ast.rs new file mode 100644 index 0000000..eb775b4 --- /dev/null +++ b/src/libvm/src/obj/syn/ast.rs @@ -0,0 +1,255 @@ +use crate::{ + inst, + reg::Reg, +}; + +pub type Ast = Vec; + +#[derive(Debug, Clone)] +pub enum Directive { + Meta(MetaSection), + Data(DataSection), + Include(String), +} + +#[derive(Debug, Clone)] +pub struct MetaSection { + pub lines: Vec, +} + +#[derive(Debug, Clone)] +pub struct MetaLine { + pub name: String, + pub value: Value, +} + +#[derive(Debug, Clone)] +pub struct DataSection { + pub name: String, + pub org: SectionOrg, + pub lines: Vec, +} + +impl DataSection { + pub fn exports(&self) -> impl Iterator { + self.lines.iter() + .filter_map(|line| if let DataLine::Export(s) = line { + Some(s.as_str()) + } else { + None + }) + } + + pub fn len(&self) -> usize { + self.lines.iter() + .map(DataLine::len) + .sum() + } +} + +#[derive(Debug, Clone)] +pub enum SectionOrg { + Start(u64), + StartEnd(u64, u64), +} + +impl SectionOrg { + pub fn start(&self) -> u64 { + match self { + SectionOrg::Start(start) | SectionOrg::StartEnd(start, _) => *start, + } + } +} + +#[derive(Debug, Clone)] +pub enum DataLine { + ValueDef(ValueDef), + Inst(Inst), + Export(String), + Label(String), +} + +impl DataLine { + pub fn len(&self) -> usize { + match self { + DataLine::ValueDef(v) => v.len(), + DataLine::Inst(i) => i.len(), + DataLine::Export(_) | DataLine::Label(_) => 0, + } + } +} + +#[derive(Debug, Clone)] +pub enum ValueDef { + Int(u64, IntSize), + String(String), + ZString(String), +} + +impl ValueDef { + pub fn len(&self) -> usize { + match self { + ValueDef::Int(_, s) => s.len(), + ValueDef::String(s) => 8 + s.as_bytes().len(), + ValueDef::ZString(s) => s.as_bytes().len() + 1, + } + } +} + +#[derive(Debug, Clone)] +pub enum Value { + // TODO : immediate int sizes + // Int(u64, IntSize) + Int(u64), + Reg(Reg), + Name(String), + Here, + Addr(Box, IntSize), +} + +impl Value { + pub fn len(&self) -> usize { + match self { + Value::Int(_) => 8, + Value::Reg(_) => 1, + Value::Name(_) => 8, + Value::Here => 8, + Value::Addr(v, _) => v.len(), + } + } + + pub fn dest_encoding(&self) -> Option { + match self { + Value::Int(_) | Value::Name(_) | Value::Here => None, + Value::Reg(_) => Some(inst::DEST_REG), + // TODO : check reg vs int value, and use dest_reg_addr8/16/32/64 values + Value::Addr(v, size) => { + if let Value::Reg(_) = &**v { + match size { + IntSize::U64 => Some(inst::DEST_REG_ADDR64), + IntSize::U32 => Some(inst::DEST_REG_ADDR32), + IntSize::U16 => Some(inst::DEST_REG_ADDR16), + IntSize::U8 => Some(inst::DEST_REG_ADDR8), + } + } else { + match size { + IntSize::U64 => Some(inst::DEST_ADDR64), + IntSize::U32 => Some(inst::DEST_ADDR32), + IntSize::U16 => Some(inst::DEST_ADDR16), + IntSize::U8 => Some(inst::DEST_ADDR8), + } + } + } + } + } + + pub fn source_encoding(&self) -> u8 { + match self { + Value::Int(_) => inst::SOURCE_IMM64, + Value::Reg(_) => inst::SOURCE_REG, + // TODO : check reg vs int value, and use source_reg_addr8/16/32/64 values + Value::Name(_) | Value::Here => inst::SOURCE_IMM64, + Value::Addr(v, size) => { + if let Value::Reg(_) = &**v { + match size { + IntSize::U64 => inst::SOURCE_REG_ADDR64, + IntSize::U32 => inst::SOURCE_REG_ADDR32, + IntSize::U16 => inst::SOURCE_REG_ADDR16, + IntSize::U8 => inst::SOURCE_REG_ADDR8, + } + } else { + match size { + IntSize::U64 => inst::SOURCE_ADDR64, + IntSize::U32 => inst::SOURCE_ADDR32, + IntSize::U16 => inst::SOURCE_ADDR16, + IntSize::U8 => inst::SOURCE_ADDR8, + } + } + } + } + } +} + +#[derive(Debug, Clone)] +pub enum IntSize { + U8, + U16, + U32, + U64, +} + +impl IntSize { + pub fn len(&self) -> usize { + match self { + IntSize::U8 => 1, + IntSize::U16 => 2, + IntSize::U32 => 4, + IntSize::U64 => 8, + } + } +} + +#[derive(Debug, Clone)] +pub enum Inst { + Add(Value, Value), + Sub(Value, Value), + Mul(Value, Value), + Div(Value, Value), + IDiv(Value, Value), + Mod(Value, Value), + And(Value, Value), + Or(Value, Value), + Xor(Value, Value), + Shl(Value, Value), + Shr(Value, Value), + INeg(Value, Value), + Inv(Value, Value), + Not(Value, Value), + CmpEq(Value, Value), + CmpLt(Value, Value), + Jmp(Value), + Jz(Value), + Jnz(Value), + Call(Value), + Ret, + Push(Value), + Pop(Value), + Mov(Value, Value), + Halt, + Nop, + Dump, +} + +impl Inst { + pub fn len(&self) -> usize { + match self { + Inst::Add(v1, v2) + | Inst::Sub(v1, v2) + | Inst::Mul(v1, v2) + | Inst::Div(v1, v2) + | Inst::IDiv(v1, v2) + | Inst::Mod(v1, v2) + | Inst::And(v1, v2) + | Inst::Or(v1, v2) + | Inst::Xor(v1, v2) + | Inst::Shl(v1, v2) + | Inst::Shr(v1, v2) + | Inst::INeg(v1, v2) + | Inst::Inv(v1, v2) + | Inst::Not(v1, v2) + | Inst::CmpEq(v1, v2) + | Inst::CmpLt(v1, v2) + | Inst::Mov(v1, v2) => { 3 + v1.len() + v2.len() } + Inst::Jmp(v) + | Inst::Jz(v) + | Inst::Jnz(v) + | Inst::Call(v) + | Inst::Push(v) + | Inst::Pop(v) => { 3 + v.len() } + Inst::Ret + | Inst::Halt + | Inst::Nop + | Inst::Dump => { 2 } + } + } +} diff --git a/src/libvm/src/obj/syn/convert.rs b/src/libvm/src/obj/syn/convert.rs new file mode 100644 index 0000000..ad913b7 --- /dev/null +++ b/src/libvm/src/obj/syn/convert.rs @@ -0,0 +1,2 @@ +struct GetLayout { +} diff --git a/src/libvm/src/obj/syn/lexer.l b/src/libvm/src/obj/syn/lexer.l new file mode 100644 index 0000000..1f1c497 --- /dev/null +++ b/src/libvm/src/obj/syn/lexer.l @@ -0,0 +1,67 @@ +%% +\$[0-9]+ "DEC_INT" +\$0[Xx][0-9a-fA-F]+ "HEX_INT" +\$0[Bb][01]+ "BIN_INT" +\.meta "DIR_META" +\.section "DIR_SECTION" +\.export "DIR_EXPORT" +\.include "DIR_INCLUDE" +\( "LPAREN" +\) "RPAREN" +\{ "LBRACE" +\} "RBRACE" +\[ "LBRACKET" +\] "RBRACKET" +\.\. "DOTDOT" +: "COLON" +, "COMMA" +\$\$ "BUCKBUCK" +u8 "U8" +u16 "U16" +u32 "U32" +u64 "U64" +\.u8 "U8_DEF" +\.u16 "U16_DEF" +\.u32 "U32_DEF" +\.u64 "U64_DEF" +\.[iu](8|16|32|64) "INT_DEF" +\.string "STR_DEF" +\.zstring "ZSTR_DEF" +"([^"]|\\[\\nt0"'])*" "STRING" +add "ADD" +sub "SUB" +mul "MUL" +div "DIV" +idiv "IDIV" +mod "MOD" +and "AND" +or "OR" +xor "XOR" +shl "SHL" +shr "SHR" +ineg "INEG" +inv "INV" +not "NOT" +cmpeq "CMPEQ" +cmplt "CMPLT" +jmp "JMP" +jz "JZ" +jnz "JNZ" +call "CALL" +ret "RET" +push "PUSH" +pop "POP" +mov "MOV" +halt "HALT" +nop "NOP" +dump "DUMP" +%ip "REG_IP" +%sp "REG_SP" +%fp "REG_FP" +%flags "REG_FLAGS" +%null "REG_NULL" +%status "REG_STATUS" +%r[0-9]{1,2} "REG_GENERAL" +[a-zA-Z_][a-zA-Z0-9_]* "NAME" +;[^\n]* ; +[ \n\t]+ ; diff --git a/src/libvm/src/obj/syn/mod.rs b/src/libvm/src/obj/syn/mod.rs new file mode 100644 index 0000000..dc74e91 --- /dev/null +++ b/src/libvm/src/obj/syn/mod.rs @@ -0,0 +1,15 @@ +pub mod ast; + +pub mod parser { + use lrpar::lrpar_mod; + lrpar_mod!("obj/syn/parser.y"); + + pub use self::parser_y::*; +} + +pub mod lexer { + use lrlex::lrlex_mod; + lrlex_mod!("obj/syn/lexer.l"); + + pub use self::lexer_l::*; +} diff --git a/src/libvm/src/obj/syn/parser.y b/src/libvm/src/obj/syn/parser.y new file mode 100644 index 0000000..dff3d4c --- /dev/null +++ b/src/libvm/src/obj/syn/parser.y @@ -0,0 +1,221 @@ +%start Top +%% + +Top -> Vec: + Top Directive { $1.push($2); $1 } + | { Vec::new() } + ; + +Directive -> Directive: + 'DIR_META' MetaBlock { Directive::Meta(MetaSection { lines: $2 }) } + | 'DIR_SECTION' Name SectionOrg DataBlock { + Directive::Data(DataSection { + name: $2, + org: $3, + lines: $4, + }) + } + | 'DIR_INCLUDE' String { Directive::Include($2) } + ; + +MetaBlock -> Vec: 'LBRACE' MetaLines 'RBRACE' { $2 }; + +MetaLines -> Vec: + MetaLines MetaLine { $1.push($2); $1 } + | { Vec::new() } + ; + + +MetaLine -> MetaLine: Name 'COLON' Value { MetaLine { name: $1, value: $3 } }; + +SectionOrg -> SectionOrg: + Int { SectionOrg::Start($1) } + | Int 'DOTDOT' Int { SectionOrg::StartEnd($1, $3) } + ; + +DataBlock -> Vec: 'LBRACE' DataLines 'RBRACE' { $2 }; + +DataLines -> Vec: + DataLines DataLine { $1.push($2); $1 } + | { Vec::new() } + ; + +DataLine -> DataLine: + ValueDef { DataLine::ValueDef($1) } + | Inst { DataLine::Inst($1) } + | 'DIR_EXPORT' Name { DataLine::Export($2) } + | Name 'COLON' { DataLine::Label($1) } + ; + +ValueDef -> ValueDef: + 'U8_DEF' Int { ValueDef::Int($2, IntSize::U8) } + | 'U16_DEF' Int { ValueDef::Int($2, IntSize::U16) } + | 'U32_DEF' Int { ValueDef::Int($2, IntSize::U32) } + | 'U64_DEF' Int { ValueDef::Int($2, IntSize::U64) } + | 'STR_DEF' String { ValueDef::String($2) } + | 'ZSTR_DEF' String { ValueDef::ZString($2) } + ; + +Value -> Value: + Int { Value::Int($1) } + | Reg { Value::Reg($1) } + | Name { Value::Name($1) } + | 'BUCKBUCK' { Value::Here } + | 'LPAREN' Value 'RPAREN' { Value::Addr(Box::new($2), IntSize::U64) } + | 'LPAREN' Value 'RPAREN' 'U8' { Value::Addr(Box::new($2), IntSize::U8) } + | 'LPAREN' Value 'RPAREN' 'U16' { Value::Addr(Box::new($2), IntSize::U16) } + | 'LPAREN' Value 'RPAREN' 'U32' { Value::Addr(Box::new($2), IntSize::U32) } + | 'LPAREN' Value 'RPAREN' 'U64' { Value::Addr(Box::new($2), IntSize::U64) } + //| 'LBRACKET' ArrayValues 'RBRACKET' { Value::Array($2) } + ; + +/* +ArrayValues -> Vec: + ArrayValues Value { $1.push($2); $1 } + | { Vec::new() } + ; +*/ + +Inst -> Inst: + 'ADD' Value 'COMMA' Value { Inst::Add($2, $4) } + | 'SUB' Value 'COMMA' Value { Inst::Sub($2, $4) } + | 'MUL' Value 'COMMA' Value { Inst::Mul($2, $4) } + | 'DIV' Value 'COMMA' Value { Inst::Div($2, $4) } + | 'IDIV' Value 'COMMA' Value { Inst::IDiv($2, $4) } + | 'MOD' Value 'COMMA' Value { Inst::Mod($2, $4) } + | 'AND' Value 'COMMA' Value { Inst::And($2, $4) } + | 'OR' Value 'COMMA' Value { Inst::Or($2, $4) } + | 'XOR' Value 'COMMA' Value { Inst::Xor($2, $4) } + | 'SHL' Value 'COMMA' Value { Inst::Shl($2, $4) } + | 'SHR' Value 'COMMA' Value { Inst::Shr($2, $4) } + | 'INEG' Value 'COMMA' Value { Inst::INeg($2, $4) } + | 'INV' Value 'COMMA' Value { Inst::Inv($2, $4) } + | 'NOT' Value 'COMMA' Value { Inst::Not($2, $4) } + | 'CMPEQ' Value 'COMMA' Value { Inst::CmpEq($2, $4) } + | 'CMPLT' Value 'COMMA' Value { Inst::CmpLt($2, $4) } + | 'JMP' Value { Inst::Jmp($2) } + | 'JZ' Value { Inst::Jz($2) } + | 'JNZ' Value { Inst::Jnz($2) } + | 'CALL' Value { Inst::Call($2) } + | 'RET' { Inst::Ret } + | 'PUSH' Value { Inst::Push($2) } + | 'POP' Value { Inst::Pop($2) } + | 'MOV' Value 'COMMA' Value { Inst::Mov($2, $4) } + | 'HALT' { Inst::Halt } + | 'NOP' { Inst::Nop } + | 'DUMP' { Inst::Dump } + ; + +Name -> String: + 'NAME' { + let v = $1.expect("could not parse name"); + $lexer.span_str(v.span()).to_string() + } + ; + +Int -> u64: + 'DEC_INT' { + let span = $1.expect("could not parse dec_int").span(); + let s = &$lexer.span_str(span)[1..]; + s.parse().unwrap() + } + | 'HEX_INT' { + let span = $1.expect("could not parse hex_int").span(); + let s = &$lexer.span_str(span)[3..]; + u64::from_str_radix(s, 16).unwrap() + } + | 'BIN_INT' { + let span = $1.expect("could not parse bin_int").span(); + let s = &$lexer.span_str(span)[3..]; + u64::from_str_radix(s, 2).unwrap() + } + ; + +Reg -> Reg: + 'REG_IP' { IP } + | 'REG_SP' { SP } + | 'REG_FP' { FP } + | 'REG_FLAGS' { FLAGS } + | 'REG_NULL' { NULL } + | 'REG_STATUS' { STATUS } + | 'REG_GENERAL' { + let v = $1.expect("could not parse reg"); + parse_reg($lexer.span_str(v.span())).unwrap() + } + ; + +String -> String: + 'STRING' { + let v = $1.expect("could not parse string"); + parse_string($lexer.span_str(v.span())) + } + ; +%% + +use crate::{ + obj::syn::ast::*, + reg::*, +}; + +fn parse_string(input: &str) -> String { + let mut s = String::new(); + let input = &input[1..input.bytes().len() - 1]; + let mut chars = input.chars(); + while let Some(c) = chars.next() { + if c == '\\' { + let next = chars.next().unwrap(); + let c = match next { + '\\' => '\\', + 'n' => '\n', + 't' => '\t', + '"' => '"', + '\'' => '\'', + '0' => '\0', + _ => unreachable!(), + }; + s.push(c); + } else { + s.push(c); + } + } + s +} + +fn parse_reg(input: &str) -> Option { + use regex::Regex; + use lazy_static::lazy_static; + lazy_static! { + static ref REG_RE: Regex = Regex::new(r"^%r([0-9]{1,2})$").unwrap(); + } + let captures = REG_RE.captures(input)?; + let reg_no: Reg = captures.get(1)? + .as_str() + .parse() + .unwrap(); + let reg = R00 + reg_no; + if reg > R31 { + None + } else { + Some(reg) + } +} + +#[cfg(test)] +mod test { + use crate::reg::*; + use super::parse_reg; + #[test] + fn test_parse_reg() { + assert_eq!(parse_reg("%r00"), Some(R00)); + assert_eq!(parse_reg("%r0"), Some(R00)); + assert_eq!(parse_reg("%r1"), Some(R01)); + assert_eq!(parse_reg("%r01"), Some(R01)); + + assert_eq!(parse_reg("%r31"), Some(R31)); + assert_eq!(parse_reg("%r32"), None); + assert_eq!(parse_reg("%r0000"), None); + assert_eq!(parse_reg("%r9"), Some(R09)); + assert_eq!(parse_reg("%r"), None); + assert_eq!(parse_reg("%r12"), Some(R12)); + } +} diff --git a/src/libvm/src/reg.rs b/src/libvm/src/reg.rs new file mode 100644 index 0000000..5edd911 --- /dev/null +++ b/src/libvm/src/reg.rs @@ -0,0 +1,77 @@ +macro_rules! registers { + { + $($variant:ident = $value:expr),* $(,)? + } => { + pub type Reg = u8; + + $( + #[allow(dead_code)] + pub const $variant: Reg = $value; + )* + + pub fn reg_name(reg: Reg) -> Option<&'static str> { + match reg { + $( + $value => Some(stringify!($variant)), + )* + _ => None, + } + } + }; +} + +registers! { + // Instruction pointer + IP = 0, + + // Stack pointer + SP = 1, + + // Frame pointer + FP = 2, + + // Flags + FLAGS = 3, + + // Zero + NULL = 4, + + // General status code + STATUS = 5, + + R00 = 6, + R01 = 7, + R02 = 8, + R03 = 9, + R04 = 10, + R05 = 11, + R06 = 12, + R07 = 13, + R08 = 14, + R09 = 15, + R10 = 16, + R11 = 17, + R12 = 18, + R13 = 19, + R14 = 20, + R15 = 21, + R16 = 22, + R17 = 23, + R18 = 24, + R19 = 25, + R20 = 26, + R21 = 27, + R22 = 28, + R23 = 29, + R24 = 30, + R25 = 31, + R26 = 32, + R27 = 33, + R28 = 34, + R29 = 35, + R30 = 36, + R31 = 37, +} + +pub const LAST_REG: Reg = 63; +pub const NUM_REGS: usize = 64; diff --git a/src/libvm/src/state.rs b/src/libvm/src/state.rs new file mode 100644 index 0000000..a09abe0 --- /dev/null +++ b/src/libvm/src/state.rs @@ -0,0 +1,387 @@ +use crate::{addr::*, error::*, flags::*, inst::*, mem::*, obj::obj::*, reg::*}; + +pub struct State { + regs: [u64; NUM_REGS], + mem: Vec, +} + +impl State { + pub fn new() -> Self { + State { + regs: [0; NUM_REGS], + mem: Default::default(), + } + } + + pub fn load_object(&mut self, object: Object, max_mem: usize) -> Result<()> { + // TODO : detecting section overlap + let mem_len = object.virtual_len(); + if mem_len > max_mem { + return Err(VmError::ObjectTooLarge { + object_size: mem_len, + max_mem, + }); + } + let mut mem = vec![0u8; max_mem]; + for section in object.sections { + match section { + Section::Data(DataSection { + name: _, + start, + len, + contents, + }) => { + for offset in 0..len { + mem[(start + offset) as usize] = contents[offset as usize]; + } + } + Section::Meta(MetaSection { entries }) => { + if let Some(addr) = entries.get("entry") { + self.set_reg_unchecked(IP, *addr); + } + } + } + } + self.mem = mem; + + Ok(()) + } + + pub fn mem_cursor(&self, addr: Addr) -> MemCursor<&[u8]> { + let mut cursor = MemCursor::new(self.mem.as_slice()); + cursor.set_position(addr); + cursor + } + + pub fn mem_cursor_mut(&mut self, addr: Addr) -> MemCursor<&mut [u8]> { + let mut cursor = MemCursor::new(self.mem.as_mut_slice()); + cursor.set_position(addr); + cursor + } + + //////////////////////////////////////////////////////////////////////////////// + // Registers + //////////////////////////////////////////////////////////////////////////////// + pub fn get_reg_unchecked(&self, reg: Reg) -> u64 { + self.regs[reg as usize] + } + + pub fn get_reg(&self, reg: Reg) -> Result { + if (reg as usize) >= NUM_REGS { + Err(VmError::IllegalReg { reg }) + } else { + Ok(self.get_reg_unchecked(reg)) + } + } + + pub fn set_reg_unchecked(&mut self, reg: Reg, value: u64) { + self.regs[reg as usize] = value; + } + + pub fn set_reg(&mut self, reg: Reg, value: u64) -> Result<()> { + if (reg as usize) >= NUM_REGS { + Err(VmError::IllegalReg { reg }) + } else { + Ok(self.set_reg_unchecked(reg, value)) + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Registers + //////////////////////////////////////////////////////////////////////////////// + + pub fn ip(&self) -> u64 { + self.get_reg_unchecked(IP) + } + + pub fn sp(&self) -> u64 { + self.get_reg_unchecked(SP) + } + + pub fn fp(&self) -> u64 { + self.get_reg_unchecked(FP) + } + + //////////////////////////////////////////////////////////////////////////////// + // Flags + //////////////////////////////////////////////////////////////////////////////// + pub fn flags(&self) -> Flags { + // this is safe because it's OK if there are random bits flipped - this shouldn't happen + // anyway, but if it does, they're ignored + unsafe { Flags::from_bits_unchecked(self.get_reg_unchecked(FLAGS)) } + } + + pub fn contains_flags(&self, flags: Flags) -> bool { + self.flags().contains(flags) + } + + pub fn insert_flags(&mut self, flags: Flags) { + let mut new_flags = self.flags(); + new_flags.insert(flags); + self.set_flags(new_flags); + } + + pub fn remove_flags(&mut self, flags: Flags) { + let mut new_flags = self.flags(); + new_flags.remove(flags); + self.set_flags(new_flags); + } + + pub fn set_flags(&mut self, flags: Flags) { + self.set_reg_unchecked(FLAGS, flags.bits()); + } + + //////////////////////////////////////////////////////////////////////////////// + // Execution + //////////////////////////////////////////////////////////////////////////////// + pub fn is_halted(&self) -> bool { + self.contains_flags(Flags::HALT) + } + + pub fn exec(&mut self) -> Result { + while !self.is_halted() { + self.tick()?; + } + + Ok(self.get_reg_unchecked(STATUS)) + } + + fn tick(&mut self) -> Result<()> { + let mut cursor = self.mem_cursor(Addr(self.ip())); + let inst = cursor.next_inst()?; + let mut next_ip = self.ip() + (inst.len() as u64); + match inst { + Inst::Add(d, s) => { + let value = self.load_dest(d)?.wrapping_add(self.load_source(s)?); + self.store_dest(d, value)?; + } + Inst::Sub(d, s) => { + let value = self.load_dest(d)?.wrapping_sub(self.load_source(s)?); + self.store_dest(d, value)?; + } + Inst::Mul(d, s) => { + let value = self.load_dest(d)?.wrapping_mul(self.load_source(s)?); + self.store_dest(d, value)?; + } + Inst::Div(d, s) => { + // TODO : catch divide by zero + let value = self.load_dest(d)?.wrapping_div(self.load_source(s)?); + self.store_dest(d, value)?; + } + Inst::IDiv(d, s) => { + // TODO : catch divide by zero + let dest = self.load_dest(d)? as i64; + let source = self.load_source(s)? as i64; + let value = dest.wrapping_div(source); + self.store_dest(d, value as u64)?; + } + Inst::Mod(d, s) => { + let value = self.load_dest(d)? % self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::And(d, s) => { + let value = self.load_dest(d)? & self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::Or(d, s) => { + let value = self.load_dest(d)? | self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::Xor(d, s) => { + let value = self.load_dest(d)? ^ self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::Shl(d, s) => { + let value = self.load_dest(d)? << self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::Shr(d, s) => { + let value = self.load_dest(d)? >> self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::INeg(d, s) => { + let value = (!self.load_source(s)?).wrapping_add(1); + self.store_dest(d, value)?; + } + Inst::Inv(d, s) => { + let value = !self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::Not(d, s) => { + let value = (self.load_source(s)? == 0) as u64; + self.store_dest(d, value)?; + } + Inst::CmpEq(s1, s2) => { + let cmp = self.load_source(s1)? == self.load_source(s2)?; + if cmp { + self.insert_flags(Flags::COMPARE); + } else { + self.remove_flags(Flags::COMPARE); + } + } + Inst::CmpLt(s1, s2) => { + let cmp = self.load_source(s1)? < self.load_source(s2)?; + if cmp { + self.insert_flags(Flags::COMPARE); + } else { + self.remove_flags(Flags::COMPARE); + } + } + Inst::Jmp(s) => { + next_ip = self.load_source(s)?; + } + Inst::Jz(s) => { + if !self.contains_flags(Flags::COMPARE) { + next_ip = self.load_source(s)?; + } + } + Inst::Jnz(s) => { + if self.contains_flags(Flags::COMPARE) { + next_ip = self.load_source(s)?; + } + } + Inst::Call(s) => { + { + let fp = self.fp(); + let ip = next_ip; + self.push(Source::Imm(fp))?; + self.push(Source::Imm(ip))?; + } + + { + let sp = self.sp(); + self.set_reg_unchecked(FP, sp - 16); + + next_ip = self.load_source(s)?; + } + } + Inst::Ret => { + let fp = self.fp(); + let sp = fp + 16; + self.set_reg_unchecked(SP, sp); + + self.pop(Dest::Reg(IP))?; + self.pop(Dest::Reg(FP))?; + + next_ip = self.ip(); + } + Inst::Push(s) => { + self.push(s)?; + } + Inst::Pop(d) => { + self.pop(d)?; + } + Inst::Mov(d, s) => { + let value = self.load_source(s)?; + self.store_dest(d, value)?; + } + Inst::Halt => { + self.insert_flags(Flags::HALT); + } + Inst::Nop => {} + Inst::Dump => { + // TODO - dump + } + } + self.set_reg_unchecked(IP, next_ip); + Ok(()) + } + + fn push(&mut self, source: Source) -> Result<()> { + let value = self.load_source(source)?; + let mut stack_addr = self.sp(); + + // create a destination based on the size of the source + let dest = match source { + Source::Addr64(_) | Source::RegAddr64(_) | Source::Reg(_) | Source::Imm(_) => Dest::Addr64(Addr(stack_addr)), + Source::Addr32(_) | Source::RegAddr32(_) => Dest::Addr32(Addr(stack_addr)), + Source::Addr16(_) | Source::RegAddr16(_) => Dest::Addr16(Addr(stack_addr)), + Source::Addr8(_) | Source::RegAddr8(_) => Dest::Addr8(Addr(stack_addr)), + }; + self.store_dest(dest, value)?; + assert_eq!(source.value_len(), dest.value_len()); + + stack_addr += source.value_len() as u64; + self.set_reg_unchecked(SP, stack_addr); + Ok(()) + } + + fn pop(&mut self, dest: Dest) -> Result<()> { + let sp = self.sp() - (dest.value_len() as u64); + + let sp_source = match dest { + Dest::Addr64(_) | Dest::RegAddr64(_) | Dest::Reg(_) => Source::Addr64(Addr(sp)), + Dest::Addr32(_) | Dest::RegAddr32(_) => Source::Addr32(Addr(sp)), + Dest::Addr16(_) | Dest::RegAddr16(_) => Source::Addr16(Addr(sp)), + Dest::Addr8(_) | Dest::RegAddr8(_) => Source::Addr8(Addr(sp)), + }; + + let value = self.load_source(sp_source)?; + + // Set the SP first, because the destination may be the SP itself + self.set_reg_unchecked(SP, sp); + self.store_dest(dest, value)?; + + Ok(()) + } + + fn store_dest(&mut self, dest: Dest, value: u64) -> Result<()> { + match dest { + Dest::Addr64(a) => self.mem_cursor_mut(a).write_u64(value), + Dest::Addr32(a) => self + .mem_cursor_mut(a) + .write_u32((value & 0xffff_ffff) as u32), + Dest::Addr16(a) => self.mem_cursor_mut(a).write_u16((value & 0xffff) as u16), + Dest::Addr8(a) => self.mem_cursor_mut(a).write_u8((value & 0xff) as u8), + Dest::RegAddr64(r) => { + let addr = Addr(self.get_reg(r)?); + self.mem_cursor_mut(addr).write_u64(value) + } + Dest::RegAddr32(r) => { + let addr = Addr(self.get_reg(r)?); + self.mem_cursor_mut(addr) + .write_u32((value & 0xffff_ffff) as u32) + } + Dest::RegAddr16(r) => { + let addr = Addr(self.get_reg(r)?); + self.mem_cursor_mut(addr).write_u16((value & 0xffff) as u16) + } + Dest::RegAddr8(r) => { + let addr = Addr(self.get_reg(r)?); + self.mem_cursor_mut(addr).write_u8((value & 0xff) as u8) + } + Dest::Reg(reg) => self.set_reg(reg, value), + } + } + + fn load_source(&self, source: Source) -> Result { + let value = match source { + Source::Addr64(a) => self.mem_cursor(a).next_u64()?, + Source::Addr32(a) => self.mem_cursor(a).next_u32()? as u64, + Source::Addr16(a) => self.mem_cursor(a).next_u16()? as u64, + Source::Addr8(a) => self.mem_cursor(a).next_u8()? as u64, + Source::RegAddr64(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u64()?, + Source::RegAddr32(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u32()? as u64, + Source::RegAddr16(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u16()? as u64, + Source::RegAddr8(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u8()? as u64, + Source::Reg(reg) => self.get_reg(reg)?, + Source::Imm(u) => u, + }; + Ok(value) + } + + fn load_dest(&self, dest: Dest) -> Result { + let value = match dest { + Dest::Addr64(a) => self.mem_cursor(a).next_u64()?, + Dest::Addr32(a) => self.mem_cursor(a).next_u32()? as u64, + Dest::Addr16(a) => self.mem_cursor(a).next_u16()? as u64, + Dest::Addr8(a) => self.mem_cursor(a).next_u8()? as u64, + Dest::RegAddr64(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u64()?, + Dest::RegAddr32(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u32()? as u64, + Dest::RegAddr16(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u16()? as u64, + Dest::RegAddr8(r) => self.mem_cursor(Addr(self.get_reg(r)?)).next_u8()? as u64, + Dest::Reg(reg) => self.get_reg(reg)?, + }; + Ok(value) + } +} diff --git a/src/libvm/src/tick.rs b/src/libvm/src/tick.rs new file mode 100644 index 0000000..2fb3a77 --- /dev/null +++ b/src/libvm/src/tick.rs @@ -0,0 +1,189 @@ +use crate::{error::*, flags::Flags, inst::*, reg::*, vm::*, visit::*, mem::MemCursor}; + +impl Vm { + pub fn tick(&mut self) -> Result<()> { + let next_ip = self.visit_inst()?; + self.set_reg(IP, next_ip); + Ok(()) + } + + fn next_ip(&self) -> Result { + let ip = self.ip(); + let op = self.get_inst_op(ip)?; + Ok(ip + (inst_len(op) as u64)) + } + + fn with_regs(&mut self, r1: Reg, r2: Reg, mapping: F) -> B + where F: FnOnce(Word, Word) -> B + { + let w1 = self.get_reg(r1); + let w2 = self.get_reg(r2); + (mapping)(w1, w2) + } +} + +impl VisitInst for Vm { + type Out = Addr; + + fn cursor(&self) -> MemCursor { + self.mem_cursor(self.ip() as usize) + } + + fn add(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1.wrapping_add(w2)); + self.set_reg(r1, out); + self.next_ip() + } + + fn mul(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1.wrapping_mul(w2)); + self.set_reg(r1, out); + self.next_ip() + } + + fn div(&mut self, r1: Reg, r2: Reg) -> Result { + // TODO : check w2 == 0 and throw error/exception + let out = self.with_regs(r1, r2, |w1, w2| w1 / w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn mod_(&mut self, r1: Reg, r2: Reg) -> Result { + // TODO : check w2 == 0 and throw error/exception + let out = self.with_regs(r1, r2, |w1, w2| w1 % w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn ineg(&mut self, r1: Reg) -> Result { + let w1 = self.get_reg(r1); + self.set_reg(r1, (!w1).wrapping_add(1)); + self.next_ip() + } + + fn and(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 & w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn or(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 | w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn inv(&mut self, r1: Reg) -> Result { + let w1 = self.get_reg(r1); + self.set_reg(r1, !w1); + self.next_ip() + } + + fn not(&mut self, r1: Reg) -> Result { + let w1 = self.get_reg(r1); + self.set_reg(r1, (w1 == 0) as Word); + self.next_ip() + } + + fn xor(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 ^ w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn shl(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 << w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn shr(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 >> w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn cmpeq(&mut self, r1: Reg, r2: Reg) -> Result { + let cmp = self.with_regs(r1, r2, |w1, w2| w1 == w2); + if cmp { + self.insert_flags(Flags::COMPARE); + } else { + self.remove_flags(Flags::COMPARE); + } + self.next_ip() + } + + fn cmplt(&mut self, r1: Reg, r2: Reg) -> Result { + let cmp = self.with_regs(r1, r2, |w1, w2| w1 < w2); + if cmp { + self.insert_flags(Flags::COMPARE); + } else { + self.remove_flags(Flags::COMPARE); + } + self.next_ip() + } + + fn jmp(&mut self, r1: Reg) -> Result { + let addr = self.get_reg(r1); + Ok(addr) + } + + fn jz(&mut self, r1: Reg) -> Result { + if !self.flags().contains(Flags::COMPARE) { + Ok(self.get_reg(r1)) + } else { + self.next_ip() + } + } + + fn jnz(&mut self, r1: Reg) -> Result { + if self.flags().contains(Flags::COMPARE) { + Ok(self.get_reg(r1)) + } else { + self.next_ip() + } + } + + fn load(&mut self, r1: Reg, r2: Reg) -> Result { + let value = Vm::load(self, r2)?; + self.set_reg_checked(r1, value)?; + self.next_ip() + } + + fn regcopy(&mut self, r1: Reg, r2: Reg) -> Result { + let value = self.get_reg_checked(r2)?; + self.set_reg_checked(r1, value)?; + self.next_ip() + } + + fn storeimm64(&mut self, r1: Reg, w1: Word) -> Result { + self.set_reg_checked(r1, w1)?; + self.next_ip() + } + + fn storeimm32(&mut self, r1: Reg, w1: HalfWord) -> Result { + self.set_reg_checked(r1, w1 as Word)?; + self.next_ip() + } + + fn memcopy(&mut self, r1: Reg, r2: Reg) -> Result { + let value = Vm::load(self, r2)?; + self.store(r1, value)?; + self.next_ip() + } + + fn store(&mut self, r1: Reg, r2: Reg) -> Result { + let value = self.get_reg_checked(r1)?; + self.store(r2, value)?; + self.next_ip() + } + + fn halt(&mut self) -> Result { + self.insert_flags(Flags::HALT); + self.next_ip() + } + + fn nop(&mut self) -> Result { + self.next_ip() + } +}