diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..2279e15c --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "r -p xtask --" diff --git a/Cargo.lock b/Cargo.lock index 24aae341..750936be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,131 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "argh" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" +dependencies = [ + "argh_shared", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "argh_shared" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +dependencies = [ + "serde", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "hbbytecode" version = "0.1.0" @@ -21,13 +146,85 @@ name = "hbxrt" version = "0.1.0" dependencies = [ "hbvm", + "nix", ] [[package]] -name = "proc-macro2" -version = "1.0.67" +name = "indenter" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -41,6 +238,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "serde" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "syn" version = "1.0.109" @@ -52,12 +284,80 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "with_builtin_macros" version = "0.0.3" @@ -75,5 +375,14 @@ checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", +] + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "argh", + "color-eyre", + "once_cell", ] diff --git a/Cargo.toml b/Cargo.toml index a648dd73..8d81c807 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "2" -members = ["hbbytecode", "hbvm", "hbxrt"] +members = ["hbbytecode", "hbvm", "hbxrt", "xtask"] diff --git a/examples/addition.hbf b/examples/addition.hbf new file mode 100644 index 00000000..38ffdea5 Binary files /dev/null and b/examples/addition.hbf differ diff --git a/examples/linux-hello.hbf b/examples/linux-hello.hbf new file mode 100644 index 00000000..d1d8616e Binary files /dev/null and b/examples/linux-hello.hbf differ diff --git a/hbbytecode/instructions.in b/hbbytecode/instructions.in index fb125dd7..8e0d1406 100644 --- a/hbbytecode/instructions.in +++ b/hbbytecode/instructions.in @@ -1,70 +1,120 @@ -// OPCODE, MNEMONIC, TYPE, DOC; - -0, UN, N, "Cause an unreachable code trap" ; -1, TX, N, "Termiante execution" ; -2, NOP, N, "Do nothing" ; - -3, ADD, RRR, "Addition" ; -4, SUB, RRR, "Subtraction" ; -5, MUL, RRR, "Multiplication" ; -6, AND, RRR, "Bitand" ; -7, OR, RRR, "Bitor" ; -8, XOR, RRR, "Bitxor" ; -9, SL, RRR, "Unsigned left bitshift" ; -10, SR, RRR, "Unsigned right bitshift" ; -11, SRS, RRR, "Signed right bitshift" ; -12, CMP, RRR, "Signed comparsion" ; -13, CMPU, RRR, "Unsigned comparsion" ; -14, DIR, RRRR, "Merged divide-remainder" ; -15, NEG, RR, "Bit negation" ; -16, NOT, RR, "Logical negation" ; -17, ADDI, RRD, "Addition with immediate" ; -18, MULI, RRD, "Multiplication with immediate" ; -19, ANDI, RRD, "Bitand with immediate" ; -20, ORI, RRD, "Bitor with immediate" ; -21, XORI, RRD, "Bitxor with immediate" ; -22, SLI, RRW, "Unsigned left bitshift with immedidate"; -23, SRI, RRW, "Unsigned right bitshift with immediate"; -24, SRSI, RRW, "Signed right bitshift with immediate" ; -25, CMPI, RRD, "Signed compare with immediate" ; -26, CMPUI, RRD, "Unsigned compare with immediate" ; - -27, CP, RR, "Copy register" ; -28, SWA, RR, "Swap registers" ; -29, LI, RD, "Load immediate" ; -30, LRA, RRO, "Load relative address" ; -31, LD, RRAH, "Load from absolute address" ; -32, ST, RRAH, "Store to absolute address" ; -33, LDR, RROH, "Load from relative address" ; -34, STR, RROH, "Store to relative address" ; -35, BMC, RRH, "Copy block of memory" ; -36, BRC, RRB, "Copy register block" ; - -37, JMP, O, "Relative jump" ; -38, JAL, RRA, "Linking absolute jump" ; -39, JALR, RRO, "Linking relative jump" ; -40, JEQ, RRP, "Branch on equal" ; -41, JNE, RRP, "Branch on nonequal" ; -42, JLT, RRP, "Branch on lesser-than (signed)" ; -43, JGT, RRP, "Branch on greater-than (signed)" ; -44, JLTU, RRP, "Branch on lesser-than (unsigned)" ; -45, JGTU, RRP, "Branch on greater-than (unsigned)" ; -46, ECA, N, "Environment call trap" ; -47, EBP, N, "Environment breakpoint" ; - -48, ADDF, RRR, "Floating addition" ; -49, SUBF, RRR, "Floating subtraction" ; -50, MULF, RRR, "Floating multiply" ; -51, DIRF, RRRR, "Merged floating divide-remainder" ; -52, FMAF, RRRR, "Fused floating multiply-add" ; -53, NEGF, RR, "Floating sign negation" ; -54, ITF, RR, "Int to float" ; -55, FTI, RR, "Float to int" ; - -56, ADDFI, RRD, "Floating addition with immediate" ; -57, MULFI, RRD, "Floating multiplication with immediate"; - -58, LRA16 , RRP, "Load relative immediate (16 bit)" ; -59, LDR16 , RRPH, "Load from relative address (16 bit)" ; -60, STR16 , RRPH, "Store to relative address (16 bit)" ; -61, JMPR16, P, "Relative jump (16 bit)" ; +0x00, UN, N, "Cause an unreachable code trap" ; +0x01, TX, N, "Termiante execution" ; +0x02, NOP, N, "Do nothing" ; +0x03, ADD8, RRR, "Addition (8b)" ; +0x04, ADD16, RRR, "Addition (16b)" ; +0x05, ADD32, RRR, "Addition (32b)" ; +0x06, ADD64, RRR, "Addition (64b)" ; +0x07, SUB8, RRR, "Subtraction (8b)" ; +0x08, SUB16, RRR, "Subtraction (16b)" ; +0x09, SUB32, RRR, "Subtraction (32b)" ; +0x0A, SUB64, RRR, "Subtraction (64b)" ; +0x0B, MUL8, RRR, "Multiplication (8b)" ; +0x0C, MUL16, RRR, "Multiplication (16b)" ; +0x0D, MUL32, RRR, "Multiplication (32b)" ; +0x0E, MUL64, RRR, "Multiplication (64b)" ; +0x0F, AND, RRR, "Bitand" ; +0x10, OR, RRR, "Bitor" ; +0x11, XOR, RRR, "Bitxor" ; +0x12, SLU8, RRR, "Unsigned left bitshift (8b)" ; +0x13, SLU16, RRR, "Unsigned left bitshift (16b)" ; +0x14, SLU32, RRR, "Unsigned left bitshift (32b)" ; +0x15, SLU64, RRR, "Unsigned left bitshift (64b)" ; +0x16, SRU8, RRR, "Unsigned right bitshift (8b)" ; +0x17, SRU16, RRR, "Unsigned right bitshift (16b)" ; +0x18, SRU32, RRR, "Unsigned right bitshift (32b)" ; +0x19, SRU64, RRR, "Unsigned right bitshift (64b)" ; +0x1A, SRS8, RRR, "Signed right bitshift (8b)" ; +0x1B, SRS16, RRR, "Signed right bitshift (16b)" ; +0x1C, SRS32, RRR, "Signed right bitshift (32b)" ; +0x1D, SRS64, RRR, "Signed right bitshift (64b)" ; +0x1E, CMP, RRR, "Signed comparsion" ; +0x1F, CMPU, RRR, "Unsigned comparsion" ; +0x20, DIRU8, RRRR, "Merged divide-remainder (unsigned 8b)" ; +0x21, DIRU16, RRRR, "Merged divide-remainder (unsigned 16b)" ; +0x22, DIRU32, RRRR, "Merged divide-remainder (unsigned 32b)" ; +0x23, DIRU64, RRRR, "Merged divide-remainder (unsigned 64b)" ; +0x24, DIRS8, RRRR, "Merged divide-remainder (signed 8b)" ; +0x25, DIRS16, RRRR, "Merged divide-remainder (signed 16b)" ; +0x26, DIRS32, RRRR, "Merged divide-remainder (signed 32b)" ; +0x27, DIRS64, RRRR, "Merged divide-remainder (signed 64b)" ; +0x28, NEG, RR, "Bit negation" ; +0x29, NOT, RR, "Logical negation" ; +0x2A, SXT8, RR, "Sign extend 8b to 64b" ; +0x2B, SXT16, RR, "Sign extend 16b to 64b" ; +0x2C, SXT32, RR, "Sign extend 32b to 64b" ; +0x2D, ADDI8, RRB, "Addition with immediate (8b)" ; +0x2E, ADDI16, RRH, "Addition with immediate (16b)" ; +0x2F, ADDI32, RRW, "Addition with immediate (32b)" ; +0x30, ADDI64, RRD, "Addition with immediate (64b)" ; +0x31, MULI8, RRW, "Multiplication with immediate (8b)" ; +0x32, MULI16, RRH, "Multiplication with immediate (16b)" ; +0x33, MULI32, RRW, "Multiplication with immediate (32b)" ; +0x34, MULI64, RRD, "Multiplication with immediate (64b)" ; +0x35, ANDI, RRD, "Bitand with immediate" ; +0x36, ORI, RRD, "Bitor with immediate" ; +0x37, XORI, RRD, "Bitxor with immediate" ; +0x38, SLUI8, RRW, "Unsigned left bitshift with immedidate (8b)" ; +0x39, SLUI16, RRW, "Unsigned left bitshift with immedidate (16b)"; +0x3A, SLUI32, RRW, "Unsigned left bitshift with immedidate (32b)"; +0x3B, SLUI64, RRW, "Unsigned left bitshift with immedidate (64b)"; +0x3C, SRUI8, RRW, "Unsigned right bitshift with immediate (8b)" ; +0x3D, SRUI16, RRW, "Unsigned right bitshift with immediate (16b)"; +0x3E, SRUI32, RRW, "Unsigned right bitshift with immediate (32b)"; +0x3F, SRUI64, RRW, "Unsigned right bitshift with immediate (64b)"; +0x40, SRSI8, RRW, "Signed right bitshift with immediate" ; +0x41, SRSI16, RRW, "Signed right bitshift with immediate" ; +0x42, SRSI32, RRW, "Signed right bitshift with immediate" ; +0x43, SRSI64, RRW, "Signed right bitshift with immediate" ; +0x44, CMPI, RRD, "Signed compare with immediate" ; +0x45, CMPUI, RRD, "Unsigned compare with immediate" ; +0x46, CP, RR, "Copy register" ; +0x47, SWA, RR, "Swap registers" ; +0x48, LI8, RB, "Load immediate (8b)" ; +0x49, LI16, RH, "Load immediate (16b)" ; +0x4A, LI32, RW, "Load immediate (32b)" ; +0x4B, LI64, RD, "Load immediate (64b)" ; +0x4C, LRA, RRO, "Load relative address" ; +0x4D, LD, RRAH, "Load from absolute address" ; +0x4E, ST, RRAH, "Store to absolute address" ; +0x4F, LDR, RROH, "Load from relative address" ; +0x50, STR, RROH, "Store to relative address" ; +0x51, BMC, RRH, "Copy block of memory" ; +0x52, BRC, RRB, "Copy register block" ; +0x53, JMP, O, "Relative jump" ; +0x54, JAL, RRA, "Linking absolute jump" ; +0x55, JALR, RRO, "Linking relative jump" ; +0x56, JEQ, RRP, "Branch on equal" ; +0x57, JNE, RRP, "Branch on nonequal" ; +0x58, JLT, RRP, "Branch on lesser-than (signed)" ; +0x59, JGT, RRP, "Branch on greater-than (signed)" ; +0x5A, JLTU, RRP, "Branch on lesser-than (unsigned)" ; +0x5B, JGTU, RRP, "Branch on greater-than (unsigned)" ; +0x5C, ECA, N, "Environment call trap" ; +0x5D, EBP, N, "Environment breakpoint" ; +0x5E, FADD32, RRR, "Floating point addition (32b)" ; +0x5F, FADD64, RRR, "Floating point addition (64b)" ; +0x60, FSUB32, RRR, "Floating point subtraction (32b)" ; +0x61, FSUB64, RRR, "Floating point subtraction (64b)" ; +0x62, FMUL32, RRR, "Floating point multiply (32b)" ; +0x63, FMUL64, RRR, "Floating point multiply (64b)" ; +0x64, FDIV32, RRR, "Floating point division (32b)" ; +0x65, FDIV64, RRR, "Floating point division (64b)" ; +0x66, FMA32, RRR, "Float fused multiply-add (32b)" ; +0x67, FMA64, RRR, "Float fused multiply-add (64b)" ; +0x68, FINV32, RR, "Float reciprocal (32b)" ; +0x69, FINV64, RR, "Float reciprocal (64b)" ; +0x6A, FCMPLT32, RRR, "Flaot compare less than (32b)" ; +0x6B, FCMPLT64, RRR, "Flaot compare less than (64b)" ; +0x6C, FCMPGT32, RRR, "Flaot compare greater than (32b)" ; +0x6D, FCMPGT64, RRR, "Flaot compare greater than (64b)" ; +0x6E, ITF32, RR, "Int to 32 bit float" ; +0x6F, ITF64, RR, "Int to 64 bit float" ; +0x70, FTI32, RRB, "Float 32 to int" ; +0x71, FTI64, RRB, "Float 64 to int" ; +0x72, FC32T64, RR, "Float 64 to Float 32" ; +0x73, FC64T32, RRB, "Float 32 to Float 64" ; +0x74, LRA16, RRP, "Load relative immediate (16 bit)" ; +0x75, LDR16, RRPH, "Load from relative address (16 bit)" ; +0x76, STR16, RRPH, "Store to relative address (16 bit)" ; +0x77, JMP16, P, "Relative jump (16 bit)" ; diff --git a/hbbytecode/src/lib.rs b/hbbytecode/src/lib.rs index 2c463ecb..21a29c68 100644 --- a/hbbytecode/src/lib.rs +++ b/hbbytecode/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +use core::convert::TryFrom; + type OpR = u8; type OpA = u64; @@ -17,6 +19,7 @@ pub unsafe trait BytecodeItem {} macro_rules! define_items { ($($name:ident ($($item:ident),* $(,)?)),* $(,)?) => { $( + #[derive(Clone, Copy, Debug)] #[repr(packed)] pub struct $name($(pub $item),*); unsafe impl BytecodeItem for $name {} @@ -31,6 +34,9 @@ define_items! { OpsRRB (OpR, OpR, OpB ), OpsRRH (OpR, OpR, OpH ), OpsRRW (OpR, OpR, OpW ), + OpsRB (OpR, OpB ), + OpsRH (OpR, OpH ), + OpsRW (OpR, OpW ), OpsRD (OpR, OpD ), OpsRRD (OpR, OpR, OpD ), OpsRRAH (OpR, OpR, OpA, OpH), @@ -89,4 +95,24 @@ macro_rules! gen_opcodes { }; } +/// Rounding mode +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum RoundingMode { + NearestEven = 0, + Truncate = 1, + Up = 2, + Down = 3, +} + +impl TryFrom for RoundingMode { + type Error = (); + + fn try_from(value: u8) -> Result { + (value >= 3) + .then(|| unsafe { core::mem::transmute(value) }) + .ok_or(()) + } +} + invoke_with_def!(gen_opcodes); diff --git a/hbvm/src/float/aarch64.rs b/hbvm/src/float/aarch64.rs new file mode 100644 index 00000000..034105d3 --- /dev/null +++ b/hbvm/src/float/aarch64.rs @@ -0,0 +1,66 @@ +use {core::arch::asm, hbbytecode::RoundingMode}; + +macro_rules! fnsdef { + {$( + $(#[$attr:meta])* + $vis:vis fn $name:ident[$inreg:ident -> $outreg:ident]($from:ident -> $to:ident): $ins:literal; + )*} => {$( + $(#[$attr])* + $vis fn $name(val: $from, mode: RoundingMode) -> $to { + let result: $to; + unsafe { + set_rounding_mode(mode); + asm!( + $ins, + out($outreg) result, + in($inreg) val, + ); + default_rounding_mode(); + } + result + } + )*}; +} + +fnsdef! { + /// Convert [`f64`] to [`f32`] with chosen rounding mode + pub fn conv64to32[vreg -> vreg](f64 -> f32): "fcvt {:s}, {:d}"; + + /// Convert [`f32`] to [`i64`] with chosen rounding mode + pub fn f32toint[vreg -> reg](f32 -> i64): "fcvtzs {}, {:s}"; + + /// Convert [`f64`] to [`i64`] with chosen rounding mode + pub fn f64toint[vreg -> reg](f64 -> i64): "fcvtzs {}, {:d}"; +} + +/// Set rounding mode +/// +/// # Safety +/// - Do not call if rounding mode isn't [`RoundingMode::NearestEven`] +/// - Do not perform any Rust FP operations until reset using +/// [`default_rounding_mode`], you have to rely on inline assembly +#[inline(always)] +unsafe fn set_rounding_mode(mode: RoundingMode) { + if mode == RoundingMode::NearestEven { + return; + } + + let fpcr: u64; + asm!("mrs {}, fpcr", out(reg) fpcr); + + let fpcr = fpcr & !(0b11 << 22) + | (match mode { + RoundingMode::NearestEven => 0b00, + RoundingMode::Truncate => 0b11, + RoundingMode::Up => 0b01, + RoundingMode::Down => 0b10, + }) << 22; + + asm!("msr fpcr, {}", in(reg) fpcr); +} + +#[inline(always)] +unsafe fn default_rounding_mode() { + // I hope so much it gets optimised + set_rounding_mode(RoundingMode::NearestEven); +} diff --git a/hbvm/src/float/mod.rs b/hbvm/src/float/mod.rs new file mode 100644 index 00000000..f8b3ea63 --- /dev/null +++ b/hbvm/src/float/mod.rs @@ -0,0 +1,17 @@ +macro_rules! arch_specific { + { + $({$($cfg:tt)*} : $mod:ident;)* + } => {$( + #[cfg($($cfg)*)] + mod $mod; + + #[cfg($($cfg)*)] + pub use $mod::*; + )*}; +} + +arch_specific! { + {target_arch = "x86_64" }: x86_64; + {target_arch = "riscv64"}: riscv64; + {target_arch = "aarch64"}: aarch64; +} diff --git a/hbvm/src/float/riscv64.rs b/hbvm/src/float/riscv64.rs new file mode 100644 index 00000000..79ba8ef7 --- /dev/null +++ b/hbvm/src/float/riscv64.rs @@ -0,0 +1,59 @@ +use {core::arch::asm, hbbytecode::RoundingMode}; + +macro_rules! roundm_op_litmode_internal { + ($ins:literal, $in:expr, $out:expr => $outy:ident, $mode:literal $(,)?) => { + asm!( + concat!($ins, " {}, {}, ", $mode), + out($outy) $out, + in(freg) $in, + ) + }; +} + +macro_rules! gen_roundm_op_litmode { + [$($ty:ident => $reg:ident),* $(,)?] => { + macro_rules! roundm_op_litmode { + $( + ($ins:literal, $in:expr, $out:expr => $ty, $mode:literal) => { + roundm_op_litmode_internal!($ins, $in, $out => $reg, $mode) + }; + )* + } + }; +} + +gen_roundm_op_litmode![ + f32 => freg, + f64 => freg, + i64 => reg, +]; + +macro_rules! fnsdef { + {$( + $(#[$attr:meta])* + $vis:vis fn $name:ident($from:ident -> $to:ident): $ins:literal; + )*} => {$( + $(#[$attr])* + $vis fn $name(val: $from, mode: RoundingMode) -> $to { + let result: $to; + unsafe { + match mode { + RoundingMode::NearestEven => roundm_op_litmode!($ins, val, result => $to, "rne"), + RoundingMode::Truncate => roundm_op_litmode!($ins, val, result => $to, "rtz"), + RoundingMode::Up => roundm_op_litmode!($ins, val, result => $to, "rup"), + RoundingMode::Down => roundm_op_litmode!($ins, val, result => $to, "rdn"), + } + } + result + } + )*}; +} + +fnsdef! { + /// Convert [`f64`] to [`f32`] with chosen rounding mode + pub fn conv64to32(f64 -> f32): "fcvt.s.d"; + /// Convert [`f32`] to [`i64`] with chosen rounding mode + pub fn f32toint(f32 -> i64): "fcvt.l.s"; + /// Convert [`f64`] to [`i64`] with chosen rounding mode + pub fn f64toint(f64 -> i64): "fcvt.l.d"; +} diff --git a/hbvm/src/float/x86_64.rs b/hbvm/src/float/x86_64.rs new file mode 100644 index 00000000..fd83bf01 --- /dev/null +++ b/hbvm/src/float/x86_64.rs @@ -0,0 +1,71 @@ +use { + core::arch::{asm, x86_64 as arin}, + hbbytecode::RoundingMode, +}; + +macro_rules! gen_op { + [$($ty:ident => $reg:ident),* $(,)?] => { + macro_rules! op { + $( + ($ins:literal, $in:expr, $out:expr => $ty) => { + asm!(concat!($ins, " {}, {}"), out($reg) $out, in(xmm_reg) $in) + }; + )* + } + }; +} + +gen_op![ + f32 => xmm_reg, + f64 => xmm_reg, + i64 => reg, +]; + +macro_rules! fnsdef { + {$( + $(#[$attr:meta])* + $vis:vis fn $name:ident($from:ident -> $to:ident): $ins:literal; + )*} => {$( + $(#[$attr])* + $vis fn $name(val: $from, mode: RoundingMode) -> $to { + let result: $to; + unsafe { + set_rounding_mode(mode); + op!($ins, val, result => $to); + default_rounding_mode(); + } + result + } + )*}; +} + +fnsdef! { + /// Convert [`f64`] to [`f32`] with chosen rounding mode + pub fn conv64to32(f64 -> f32): "cvtsd2ss"; + /// Convert [`f32`] to [`i64`] with chosen rounding mode + pub fn f32toint(f32 -> i64): "cvttss2si"; + /// Convert [`f64`] to [`i64`] with chosen rounding mode + pub fn f64toint(f64 -> i64): "cvttsd2si"; +} + +/// Set rounding mode +/// +/// # Safety +/// - Do not call if rounding mode isn't [`RoundingMode::NearestEven`] +/// - Do not perform any Rust FP operations until reset using +/// [`default_rounding_mode`], you have to rely on inline assembly +#[inline(always)] +unsafe fn set_rounding_mode(mode: RoundingMode) { + arin::_MM_SET_ROUNDING_MODE(match mode { + RoundingMode::NearestEven => return, + RoundingMode::Truncate => arin::_MM_ROUND_TOWARD_ZERO, + RoundingMode::Up => arin::_MM_ROUND_UP, + RoundingMode::Down => arin::_MM_ROUND_DOWN, + }) +} + +#[inline(always)] +fn default_rounding_mode() { + // SAFETY: This is said to be the default mode, do not trust me. + unsafe { arin::_MM_SET_ROUNDING_MODE(arin::_MM_ROUND_NEAREST) }; +} diff --git a/hbvm/src/lib.rs b/hbvm/src/lib.rs index 3da37de7..5b90f83f 100644 --- a/hbvm/src/lib.rs +++ b/hbvm/src/lib.rs @@ -12,7 +12,6 @@ #![no_std] #![cfg_attr(feature = "nightly", feature(fn_align))] -#![warn(missing_docs)] use mem::{Address, Memory}; @@ -23,6 +22,7 @@ pub mod mem; pub mod value; mod bmc; +mod float; mod utils; mod vmrun; @@ -92,6 +92,12 @@ pub enum VmRunError { /// Reached unreachable code Unreachable, + + /// Invalid operand + InvalidOperand, + + /// Unimplemented feature + Unimplemented, } /// Virtual machine halt ok diff --git a/hbvm/src/mem/mod.rs b/hbvm/src/mem/mod.rs index f8d010dc..7387abee 100644 --- a/hbvm/src/mem/mod.rs +++ b/hbvm/src/mem/mod.rs @@ -6,7 +6,7 @@ pub(crate) mod addr; pub use addr::Address; -use {crate::utils::impl_display, hbbytecode::BytecodeItem}; +use crate::utils::impl_display; /// Load-store memory access pub trait Memory { @@ -36,13 +36,7 @@ pub trait Memory { /// /// # Safety /// - Data read have to be valid - unsafe fn prog_read(&mut self, addr: Address) -> Option; - - /// Read from program memory to exectue - /// - /// # Safety - /// - You have to be really sure that these bytes are there, understand? - unsafe fn prog_read_unchecked(&mut self, addr: Address) -> T; + unsafe fn prog_read(&mut self, addr: Address) -> T; } /// Unhandled load access trap diff --git a/hbvm/src/mem/softpaging/mod.rs b/hbvm/src/mem/softpaging/mod.rs index 331ad4d7..3e1f6de9 100644 --- a/hbvm/src/mem/softpaging/mod.rs +++ b/hbvm/src/mem/softpaging/mod.rs @@ -78,31 +78,19 @@ impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory } #[inline(always)] - unsafe fn prog_read(&mut self, addr: Address) -> Option { + unsafe fn prog_read(&mut self, addr: Address) -> T { if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() { - return self.icache.fetch::(addr, self.root_pt); + return self + .icache + .fetch::(addr, self.root_pt) + .unwrap_or_else(|| unsafe { core::mem::zeroed() }); } let addr = addr.truncate_usize(); self.program .get(addr..addr + size_of::()) .map(|x| x.as_ptr().cast::().read()) - } - - #[inline(always)] - unsafe fn prog_read_unchecked(&mut self, addr: Address) -> T { - if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() { - return self - .icache - .fetch::(addr, self.root_pt) - .unwrap_or_else(|| core::mem::zeroed()); - } - - self.program - .as_ptr() - .add(addr.truncate_usize()) - .cast::() - .read() + .unwrap_or_else(|| unsafe { core::mem::zeroed() }) } } diff --git a/hbvm/src/utils.rs b/hbvm/src/utils.rs index 19830dbf..fbd43af9 100644 --- a/hbvm/src/utils.rs +++ b/hbvm/src/utils.rs @@ -46,8 +46,8 @@ pub(crate) mod internal { pub(crate) use impl_display_match_fragment; } -macro_rules! static_assert_eq(($l:expr, $r:expr $(,)?) => { - const _: [(); ($l != $r) as usize] = []; +macro_rules! static_assert(($expr:expr $(,)?) => { + const _: [(); !$expr as usize] = []; }); -pub(crate) use {impl_display, static_assert_eq}; +pub(crate) use {impl_display, static_assert}; diff --git a/hbvm/src/value.rs b/hbvm/src/value.rs index b72b72b5..fd74b7b6 100644 --- a/hbvm/src/value.rs +++ b/hbvm/src/value.rs @@ -1,35 +1,32 @@ //! HoleyBytes register value definition -/// Define [`Value`] union +use crate::utils::static_assert; + +/// Define [`Value`] »union« (it's fake) /// /// # Safety -/// Union variants have to be sound to byte-reinterpretate +/// Its variants have to be sound to byte-reinterpretate /// between each other. Otherwise the behaviour is undefined. macro_rules! value_def { ($($ty:ident),* $(,)?) => { /// HBVM register value #[derive(Copy, Clone)] - #[repr(packed)] - pub union Value { - $( - #[doc = concat!(stringify!($ty), " type")] - pub $ty: $ty - ),* - } - + #[repr(transparent)] + pub struct Value(pub u64); $( impl From<$ty> for Value { #[inline] fn from(value: $ty) -> Self { - Self { $ty: value } + let mut new = core::mem::MaybeUninit::::zeroed(); + unsafe { + new.as_mut_ptr().cast::<$ty>().write(value); + Self(new.assume_init()) + } } } - crate::utils::static_assert_eq!( - core::mem::size_of::<$ty>(), - core::mem::size_of::(), - ); + static_assert!(core::mem::size_of::<$ty>() <= core::mem::size_of::()); impl private::Sealed for $ty {} unsafe impl ValueVariant for $ty {} @@ -41,23 +38,7 @@ impl Value { /// Byte reinterpret value to target variant #[inline] pub fn cast(self) -> V { - /// Evil. - /// - /// Transmute cannot be performed with generic type - /// as size is unknown, so union is used. - /// - /// # Safety - /// If [`ValueVariant`] implemented correctly, it's fine :) - /// - /// :ferrisClueless: - union Transmute { - /// Self - src: Value, - /// Target variant - variant: Variant, - } - - unsafe { Transmute { src: self }.variant } + unsafe { core::mem::transmute_copy(&self.0) } } } @@ -69,8 +50,8 @@ mod private { pub trait Sealed {} } -value_def!(u64, i64, f64); -crate::utils::static_assert_eq!(core::mem::size_of::(), 8); +value_def!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64); +static_assert!(core::mem::size_of::() == 8); impl core::fmt::Debug for Value { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -78,3 +59,28 @@ impl core::fmt::Debug for Value { write!(f, "{:x}", self.cast::()) } } + +pub(crate) trait CheckedDivRem { + fn checked_div(self, other: Self) -> Option + where + Self: Sized; + fn checked_rem(self, other: Self) -> Option + where + Self: Sized; +} + +macro_rules! impl_checked_div_rem { + ($($ty:ty),* $(,)?) => { + $(impl CheckedDivRem for $ty { + #[inline(always)] + fn checked_div(self, another: Self) -> Option + { self.checked_div(another) } + + #[inline(always)] + fn checked_rem(self, another: Self) -> Option + { self.checked_rem(another) } + })* + }; +} + +impl_checked_div_rem!(u8, u16, u32, u64, i8, i16, i32, i64); diff --git a/hbvm/src/vmrun.rs b/hbvm/src/vmrun.rs index e633e24b..965ebab0 100644 --- a/hbvm/src/vmrun.rs +++ b/hbvm/src/vmrun.rs @@ -2,7 +2,7 @@ //! //! Have fun -use hbbytecode::OpsN; +use hbbytecode::RoundingMode; use { super::{ @@ -11,19 +11,20 @@ use { value::{Value, ValueVariant}, Vm, VmRunError, VmRunOk, }, - crate::mem::Address, + crate::{mem::Address, value::CheckedDivRem}, core::{cmp::Ordering, ops}, hbbytecode::{ - BytecodeItem, OpsO, OpsP, OpsRD, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO, OpsRROH, - OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW, + OpsN, OpsO, OpsP, OpsRB, OpsRD, OpsRH, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO, + OpsRROH, OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW, OpsRW, }, }; macro_rules! handler { ($self:expr, |$ty:ident ($($ident:pat),* $(,)?)| $expr:expr) => {{ let $ty($($ident),*) = $self.decode::<$ty>(); - #[allow(clippy::no_effect)] $expr; + #[allow(clippy::no_effect)] let e = $expr; $self.bump_pc::<$ty>(); + e }}; } @@ -58,11 +59,7 @@ where // - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU // sorry 8 bit fans, HBVM won't run on your Speccy :( unsafe { - match self - .memory - .prog_read::(self.pc as _) - .ok_or(VmRunError::ProgramFetchLoadEx(self.pc as _))? - { + match self.memory.prog_read::(self.pc as _) { UN => { self.bump_pc::(); return Err(VmRunError::Unreachable); @@ -72,15 +69,32 @@ where return Ok(VmRunOk::End); } NOP => handler!(self, |OpsN()| ()), - ADD => self.binary_op(u64::wrapping_add), - SUB => self.binary_op(u64::wrapping_sub), - MUL => self.binary_op(u64::wrapping_mul), + ADD8 => self.binary_op(u8::wrapping_add), + ADD16 => self.binary_op(u16::wrapping_add), + ADD32 => self.binary_op(u32::wrapping_add), + ADD64 => self.binary_op(u64::wrapping_add), + SUB8 => self.binary_op(u8::wrapping_sub), + SUB16 => self.binary_op(u16::wrapping_sub), + SUB32 => self.binary_op(u32::wrapping_sub), + SUB64 => self.binary_op(u64::wrapping_sub), + MUL8 => self.binary_op(u8::wrapping_mul), + MUL16 => self.binary_op(u16::wrapping_mul), + MUL32 => self.binary_op(u32::wrapping_mul), + MUL64 => self.binary_op(u64::wrapping_mul), AND => self.binary_op::(ops::BitAnd::bitand), OR => self.binary_op::(ops::BitOr::bitor), XOR => self.binary_op::(ops::BitXor::bitxor), - SL => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)), - SR => self.binary_op(|l, r| u64::wrapping_shr(l, r as u32)), - SRS => self.binary_op(|l: u64, r| i64::wrapping_shl(l as i64, r as u32) as u64), + SLU8 => self.binary_op(|l, r| u8::wrapping_shl(l, r as u32)), + SLU16 => self.binary_op(|l, r| u16::wrapping_shl(l, r as u32)), + SLU32 => self.binary_op(u32::wrapping_shl), + SLU64 => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)), + SRU8 => self.binary_op(|l, r| u8::wrapping_shr(l, r as u32)), + SRU16 => self.binary_op(|l, r| u16::wrapping_shr(l, r as u32)), + SRU32 => self.binary_op(u32::wrapping_shr), + SRS8 => self.binary_op(|l: i8, r| i8::wrapping_shl(l, r as u32)), + SRS16 => self.binary_op(|l: i16, r| i16::wrapping_shl(l, r as u32)), + SRS32 => self.binary_op(|l: i32, r| i32::wrapping_shl(l, r as u32)), + SRS64 => self.binary_op(|l: i64, r| i64::wrapping_shl(l, r as u32)), CMP => handler!(self, |OpsRRR(tg, a0, a1)| { // Compare a0 <=> a1 // < → 0 @@ -107,6 +121,14 @@ where + 1, ); }), + DIRU8 => self.dir::(), + DIRU16 => self.dir::(), + DIRU32 => self.dir::(), + DIRU64 => self.dir::(), + DIRS8 => self.dir::(), + DIRS16 => self.dir::(), + DIRS32 => self.dir::(), + DIRS64 => self.dir::(), NEG => handler!(self, |OpsRR(tg, a0)| { // Bit negation self.write_reg(tg, !self.read_reg(a0).cast::()) @@ -115,21 +137,38 @@ where // Logical negation self.write_reg(tg, u64::from(self.read_reg(a0).cast::() == 0)); }), - DIR => handler!(self, |OpsRRRR(dt, rt, a0, a1)| { - // Fused Division-Remainder - let a0 = self.read_reg(a0).cast::(); - let a1 = self.read_reg(a1).cast::(); - self.write_reg(dt, a0.checked_div(a1).unwrap_or(u64::MAX)); - self.write_reg(rt, a0.checked_rem(a1).unwrap_or(u64::MAX)); + SXT8 => handler!(self, |OpsRR(tg, a0)| { + self.write_reg(tg, self.read_reg(a0).cast::() as i64) }), - ADDI => self.binary_op_imm(u64::wrapping_add), - MULI => self.binary_op_imm(u64::wrapping_sub), + SXT16 => handler!(self, |OpsRR(tg, a0)| { + self.write_reg(tg, self.read_reg(a0).cast::() as i64) + }), + SXT32 => handler!(self, |OpsRR(tg, a0)| { + self.write_reg(tg, self.read_reg(a0).cast::() as i64) + }), + ADDI8 => self.binary_op_imm(u8::wrapping_add), + ADDI16 => self.binary_op_imm(u16::wrapping_add), + ADDI32 => self.binary_op_imm(u32::wrapping_add), + ADDI64 => self.binary_op_imm(u64::wrapping_add), + MULI8 => self.binary_op_imm(u8::wrapping_sub), + MULI16 => self.binary_op_imm(u16::wrapping_sub), + MULI32 => self.binary_op_imm(u32::wrapping_sub), + MULI64 => self.binary_op_imm(u64::wrapping_sub), ANDI => self.binary_op_imm::(ops::BitAnd::bitand), ORI => self.binary_op_imm::(ops::BitOr::bitor), XORI => self.binary_op_imm::(ops::BitXor::bitxor), - SLI => self.binary_op_ims(u64::wrapping_shl), - SRI => self.binary_op_ims(u64::wrapping_shr), - SRSI => self.binary_op_ims(i64::wrapping_shr), + SLUI8 => self.binary_op_ims::(ops::Shl::shl), + SLUI16 => self.binary_op_ims::(ops::Shl::shl), + SLUI32 => self.binary_op_ims::(ops::Shl::shl), + SLUI64 => self.binary_op_ims::(ops::Shl::shl), + SRUI8 => self.binary_op_ims::(ops::Shr::shr), + SRUI16 => self.binary_op_ims::(ops::Shr::shr), + SRUI32 => self.binary_op_ims::(ops::Shr::shr), + SRUI64 => self.binary_op_ims::(ops::Shr::shr), + SRSI8 => self.binary_op_ims::(ops::Shr::shr), + SRSI16 => self.binary_op_ims::(ops::Shr::shr), + SRSI32 => self.binary_op_ims::(ops::Shr::shr), + SRSI64 => self.binary_op_ims::(ops::Shr::shr), CMPI => handler!(self, |OpsRRD(tg, a0, imm)| { self.write_reg( tg, @@ -158,9 +197,10 @@ where } } }), - LI => handler!(self, |OpsRD(tg, imm)| { - self.write_reg(tg, imm); - }), + LI8 => handler!(self, |OpsRB(tg, imm)| self.write_reg(tg, imm)), + LI16 => handler!(self, |OpsRH(tg, imm)| self.write_reg(tg, imm)), + LI32 => handler!(self, |OpsRW(tg, imm)| self.write_reg(tg, imm)), + LI64 => handler!(self, |OpsRD(tg, imm)| self.write_reg(tg, imm)), LRA => handler!(self, |OpsRRO(tg, reg, imm)| { self.write_reg( tg, @@ -272,34 +312,65 @@ where self.bump_pc::(); return Ok(VmRunOk::Breakpoint); } - ADDF => self.binary_op::(ops::Add::add), - SUBF => self.binary_op::(ops::Sub::sub), - MULF => self.binary_op::(ops::Mul::mul), - DIRF => handler!(self, |OpsRRRR(dt, rt, a0, a1)| { - let a0 = self.read_reg(a0).cast::(); - let a1 = self.read_reg(a1).cast::(); - self.write_reg(dt, a0 / a1); - self.write_reg(rt, a0 % a1); + FADD32 => self.binary_op::(ops::Add::add), + FADD64 => self.binary_op::(ops::Add::add), + FSUB32 => self.binary_op::(ops::Sub::sub), + FSUB64 => self.binary_op::(ops::Sub::sub), + FMUL32 => self.binary_op::(ops::Mul::mul), + FMUL64 => self.binary_op::(ops::Mul::mul), + FDIV32 => self.binary_op::(ops::Div::div), + FDIV64 => self.binary_op::(ops::Div::div), + FMA32 => self.fma::(), + FMA64 => self.fma::(), + FINV32 => handler!(self, |OpsRR(tg, reg)| { + self.write_reg(tg, 1. / self.read_reg(reg).cast::()) }), - FMAF => handler!(self, |OpsRRRR(dt, a0, a1, a2)| { + FINV64 => handler!(self, |OpsRR(tg, reg)| { + self.write_reg(tg, 1. / self.read_reg(reg).cast::()) + }), + FCMPLT32 => self.fcmp::(Ordering::Less), + FCMPLT64 => self.fcmp::(Ordering::Less), + FCMPGT32 => self.fcmp::(Ordering::Greater), + FCMPGT64 => self.fcmp::(Ordering::Greater), + ITF32 => handler!(self, |OpsRR(tg, reg)| { + self.write_reg(tg, self.read_reg(reg).cast::() as f32); + }), + ITF64 => handler!(self, |OpsRR(tg, reg)| { + self.write_reg(tg, self.read_reg(reg).cast::() as f64); + }), + FTI32 => handler!(self, |OpsRRB(tg, reg, mode)| { self.write_reg( - dt, - self.read_reg(a0).cast::() * self.read_reg(a1).cast::() - + self.read_reg(a2).cast::(), + tg, + crate::float::f32toint( + self.read_reg(reg).cast::(), + RoundingMode::try_from(mode) + .map_err(|()| VmRunError::InvalidOperand)?, + ), ); }), - NEGF => handler!(self, |OpsRR(dt, a0)| { - self.write_reg(dt, -self.read_reg(a0).cast::()); + FTI64 => handler!(self, |OpsRRB(tg, reg, mode)| { + self.write_reg( + tg, + crate::float::f64toint( + self.read_reg(reg).cast::(), + RoundingMode::try_from(mode) + .map_err(|()| VmRunError::InvalidOperand)?, + ), + ); }), - ITF => handler!(self, |OpsRR(dt, a0)| { - self.write_reg(dt, self.read_reg(a0).cast::() as f64); + FC32T64 => handler!(self, |OpsRR(tg, reg)| { + self.write_reg(tg, self.read_reg(reg).cast::() as f64); + }), + FC64T32 => handler!(self, |OpsRRB(tg, reg, mode)| { + self.write_reg( + tg, + crate::float::conv64to32( + self.read_reg(reg).cast(), + RoundingMode::try_from(mode) + .map_err(|()| VmRunError::InvalidOperand)?, + ), + ) }), - FTI => { - let OpsRR(dt, a0) = self.decode(); - self.write_reg(dt, self.read_reg(a0).cast::() as i64); - } - ADDFI => self.binary_op_imm::(ops::Add::add), - MULFI => self.binary_op_imm::(ops::Mul::mul), LRA16 => handler!(self, |OpsRRP(tg, reg, imm)| { self.write_reg( tg, @@ -312,7 +383,7 @@ where STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| { self.store(dst, base, u64::from(off).wrapping_add(self.pc.get()), count)?; }), - JMPR16 => handler!(self, |OpsP(off)| self.pc = self.pc.wrapping_add(off)), + JMP16 => handler!(self, |OpsP(off)| self.pc = self.pc.wrapping_add(off)), op => return Err(VmRunError::InvalidOpcode(op)), } } @@ -328,14 +399,14 @@ where /// Bump instruction pointer #[inline(always)] - fn bump_pc(&mut self) { + fn bump_pc(&mut self) { self.pc = self.pc.wrapping_add(core::mem::size_of::() + 1); } /// Decode instruction operands #[inline(always)] - unsafe fn decode(&mut self) -> T { - self.memory.prog_read_unchecked::(self.pc + 1_u64) + unsafe fn decode(&mut self) -> T { + self.memory.prog_read::(self.pc + 1_u64) } /// Load @@ -395,12 +466,11 @@ where /// Perform binary operation over register and immediate #[inline(always)] unsafe fn binary_op_imm(&mut self, op: impl Fn(T, T) -> T) { - let OpsRRD(tg, reg, imm) = self.decode(); - self.write_reg( - tg, - op(self.read_reg(reg).cast::(), Value::from(imm).cast::()), - ); + let OpsRR(tg, reg) = self.decode(); + let imm: T = self.decode(); + self.write_reg(tg, op(self.read_reg(reg).cast::(), imm)); self.bump_pc::(); + self.bump_pc::(); } /// Perform binary operation over register and shift immediate @@ -411,6 +481,51 @@ where self.bump_pc::(); } + /// Fused division-remainder + #[inline(always)] + unsafe fn dir(&mut self) { + handler!(self, |OpsRRRR(td, tr, a0, a1)| { + let a0 = self.read_reg(a0).cast::(); + let a1 = self.read_reg(a1).cast::(); + + if let Some(div) = a0.checked_div(a1) { + self.write_reg(td, div); + } else { + self.write_reg(td, -1_i64); + } + + if let Some(rem) = a0.checked_rem(a1) { + self.write_reg(tr, rem); + } else { + self.write_reg(tr, a0); + } + }); + } + + /// Fused multiply-add + #[inline(always)] + unsafe fn fma(&mut self) + where + T: ValueVariant + core::ops::Mul + core::ops::Add, + { + handler!(self, |OpsRRRR(tg, a0, a1, a2)| { + let a0 = self.read_reg(a0).cast::(); + let a1 = self.read_reg(a1).cast::(); + let a2 = self.read_reg(a2).cast::(); + self.write_reg(tg, a0 * a1 + a2) + }); + } + + /// Float comparsion + #[inline(always)] + unsafe fn fcmp(&mut self, nan: Ordering) { + handler!(self, |OpsRRR(tg, a0, a1)| { + let a0 = self.read_reg(a0).cast::(); + let a1 = self.read_reg(a1).cast::(); + self.write_reg(tg, (a0.partial_cmp(&a1).unwrap_or(nan) as i8 + 1) as u8) + }); + } + /// Jump at `PC + #3` if ordering on `#0 <=> #1` is equal to expected #[inline(always)] unsafe fn cond_jmp(&mut self, expected: Ordering) { diff --git a/hbxrt/Cargo.toml b/hbxrt/Cargo.toml index d6b1792b..0dbf35ea 100644 --- a/hbxrt/Cargo.toml +++ b/hbxrt/Cargo.toml @@ -1,7 +1,9 @@ [package] -name = "hbxrt" -version = "0.1.0" -edition = "2021" +name = "hbxrt" +version = "0.1.0" +edition = "2021" +default-run = "hbxrt" [dependencies] hbvm.path = "../hbvm" +nix = { version = "0.27", features = ["mman"] } diff --git a/hbxrt/src/main.rs b/hbxrt/src/main.rs index 8f5a0216..9be88add 100644 --- a/hbxrt/src/main.rs +++ b/hbxrt/src/main.rs @@ -1,11 +1,69 @@ -use std::io::{stdin, Read}; +//! Holey Bytes Experimental Runtime -/// Holey Bytes Experimental Runtime +mod mem; + +use { + hbvm::{mem::Address, Vm, VmRunOk}, + nix::sys::mman::{mmap, MapFlags, ProtFlags}, + std::{env::args, fs::File, num::NonZeroUsize, process::exit}, +}; fn main() -> Result<(), Box> { - let mut prog = vec![]; - stdin().read_to_end(&mut prog)?; + eprintln!("== HB×RT (Holey Bytes Linux Runtime) v0.1 =="); + eprintln!("[W] Currently supporting only flat images"); + + let Some(image_path) = args().nth(1) else { + eprintln!("[E] Missing image path"); + exit(1); + }; + + // Load program + eprintln!("[I] Loading image from \"{image_path}\""); + let file = File::open(image_path)?; + let ptr = unsafe { + mmap( + None, + NonZeroUsize::new(file.metadata()?.len() as usize).ok_or("File is empty")?, + ProtFlags::PROT_READ, + MapFlags::MAP_PRIVATE, + Some(&file), + 0, + )? + }; + + eprintln!("[I] Image loaded at {ptr:p}"); + + // Execute program + let mut vm = unsafe { Vm::<_, 0>::new(mem::HostMemory, Address::new(ptr as u64)) }; + let stat = loop { + match vm.run() { + Ok(VmRunOk::Breakpoint) => eprintln!( + "[I] Hit breakpoint\nIP: {}\n== Registers ==\n{:?}", + vm.pc, vm.registers + ), + Ok(VmRunOk::Timer) => (), + Ok(VmRunOk::Ecall) => unsafe { + std::arch::asm!( + "syscall", + inlateout("rax") vm.registers[1].0, + in("rdi") vm.registers[2].0, + in("rsi") vm.registers[3].0, + in("rdx") vm.registers[4].0, + in("r10") vm.registers[5].0, + in("r8") vm.registers[6].0, + in("r9") vm.registers[7].0, + ) + }, + Ok(VmRunOk::End) => break Ok(()), + Err(e) => break Err(e), + } + }; + + eprintln!("\n== Registers ==\n{:?}", vm.registers); + if let Err(e) = stat { + eprintln!("\n[E] Runtime error: {e:?}"); + exit(2); + } - eprintln!("WARNING! Bytecode valider has not been yet implemented and running program can lead to undefiend behaviour."); Ok(()) } diff --git a/hbxrt/src/mem.rs b/hbxrt/src/mem.rs new file mode 100644 index 00000000..e1687d31 --- /dev/null +++ b/hbxrt/src/mem.rs @@ -0,0 +1,31 @@ +use hbvm::mem::{Address, LoadError, Memory, StoreError}; + +pub struct HostMemory; +impl Memory for HostMemory { + #[inline] + unsafe fn load( + &mut self, + addr: Address, + target: *mut u8, + count: usize, + ) -> Result<(), LoadError> { + unsafe { core::ptr::copy(addr.get() as *const u8, target, count) } + Ok(()) + } + + #[inline] + unsafe fn store( + &mut self, + addr: Address, + source: *const u8, + count: usize, + ) -> Result<(), StoreError> { + unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) } + Ok(()) + } + + #[inline] + unsafe fn prog_read(&mut self, addr: Address) -> T { + core::ptr::read(addr.get() as *const T) + } +} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..cde8bef2 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +argh = "0.1" +color-eyre = "0.6" +once_cell = "1.18" diff --git a/xtask/src/fmt.rs b/xtask/src/fmt.rs new file mode 100644 index 00000000..4cce8d73 --- /dev/null +++ b/xtask/src/fmt.rs @@ -0,0 +1,93 @@ +use { + crate::{utils::IterExt, ROOT}, + argh::FromArgs, + color_eyre::{eyre::eyre, Result}, + std::{ + fs::File, + io::{BufRead, BufReader, BufWriter, Seek, Write}, + }, +}; + +/// Format `instructions.in` +#[derive(Debug, FromArgs, PartialEq)] +#[argh(subcommand, name = "fmt")] +pub struct Command { + /// renumber instructions in their definition order + #[argh(switch, short = 'r')] + renumber: bool, +} + +pub fn command(args: Command) -> Result<()> { + let mut file = File::options() + .read(true) + .write(true) + .open(ROOT.join("hbbytecode/instructions.in"))?; + + // Extract records + let reader = BufReader::new(&file); + let mut recs = vec![]; + let mut lens = [0_usize; 4]; + + for rec in reader.split(b';').filter_map(|r| { + r.map(|ln| { + let s = String::from_utf8_lossy(&ln); + let s = s.trim_matches('\n'); + if s.is_empty() { + return None; + } + + s.split(',') + .map(|s| Box::::from(s.trim())) + .collect_array::<4>() + .map(Ok::<_, ()>) + }) + .transpose() + }) { + let rec = rec?.map_err(|_| eyre!("Invalid record format"))?; + for (current, next) in lens.iter_mut().zip(rec.iter()) { + *current = (*current).max(next.len()); + } + + recs.push(rec); + } + + // Clear file! + file.set_len(0)?; + file.seek(std::io::SeekFrom::Start(0))?; + + let mut writer = BufWriter::new(file); + + let ord_opco_len = digit_count(recs.len()) as usize; + for (n, rec) in recs.iter().enumerate() { + // Write opcode number + if args.renumber { + let n = format!("{n:#04X}"); + write!(writer, "{n}, {}", padding(ord_opco_len, &n))?; + } else { + write!(writer, "{}, {}", rec[0], padding(lens[0], &rec[0]))?; + } + + // Write other fields + writeln!( + writer, + "{}, {}{},{} {}{};", + rec[1], + padding(lens[1], &rec[1]), + rec[2], + padding(lens[2], &rec[2]), + rec[3], + padding(lens[3], &rec[3]), + )?; + } + + Ok(()) +} + +fn padding(req: usize, s: &str) -> Box { + " ".repeat(req.saturating_sub(s.len())).into() +} + +#[inline] +fn digit_count(n: usize) -> u32 { + n.checked_ilog10().unwrap_or(0) + 1 +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..8468a8fc --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,25 @@ +mod fmt; +mod utils; + +use {argh::FromArgs, color_eyre::Result, once_cell::sync::Lazy, std::path::Path}; + +static ROOT: Lazy<&Path> = Lazy::new(|| Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap()); + +/// xTask for Holey Bytes project +#[derive(FromArgs)] +struct Command { + #[argh(subcommand)] + subcom: Subcommands, +} + +#[derive(FromArgs)] +#[argh(subcommand)] +enum Subcommands { + Format(fmt::Command), +} + +fn main() -> Result<()> { + match argh::from_env::().subcom { + Subcommands::Format(com) => fmt::command(com), + } +} diff --git a/xtask/src/utils.rs b/xtask/src/utils.rs new file mode 100644 index 00000000..8ad0081e --- /dev/null +++ b/xtask/src/utils.rs @@ -0,0 +1,19 @@ +use std::mem::MaybeUninit; + +pub trait IterExt: Iterator { + fn collect_array(&mut self) -> Option<[Self::Item; N]> + where + Self: Sized, + { + let mut array: [MaybeUninit; N] = + unsafe { MaybeUninit::uninit().assume_init() }; + + for item in &mut array { + item.write(self.next()?); + } + + Some(array.map(|item| unsafe { item.assume_init() })) + } +} + +impl IterExt for T {}