From 3471b7f9d04eaf7b2d698036cf19d5607370ae73 Mon Sep 17 00:00:00 2001 From: Xavier Routh <xrouth2@illinois.edu> Date: Tue, 10 Sep 2024 14:22:26 -0500 Subject: [PATCH] interpreter --- .gitignore | 1 + Cargo.lock | 732 ++------------- Cargo.toml | 7 +- hercules_samples/call.hir | 7 + hercules_test/hercules_interpreter/Cargo.toml | 14 + .../hercules_interpreter/src/interpreter.rs | 677 ++++++++++++++ hercules_test/hercules_interpreter/src/lib.rs | 174 ++++ .../hercules_interpreter/src/main.rs | 33 + .../hercules_interpreter/src/value.rs | 852 ++++++++++++++++++ hercules_test/hercules_tests/Cargo.toml | 14 + .../hercules_tests/tests/opt_tests.rs | 201 +++++ hercules_test/test_inputs/call.hir | 7 + hercules_test/test_inputs/call_dc_params.hir | 9 + hercules_test/test_inputs/ccp_example.hir | 19 + hercules_test/test_inputs/fork_join.hir | 8 + hercules_test/test_inputs/gvn_example.hir | 8 + hercules_test/test_inputs/matmul.hir | 58 ++ hercules_test/test_inputs/matmul_int.hir | 21 + hercules_test/test_inputs/simple1.hir | 3 + hercules_test/test_inputs/simple2.hir | 13 + hercules_test/test_inputs/simple3.hir | 11 + hercules_test/test_inputs/strset.hir | 17 + hercules_test/test_inputs/sum_int1.hir | 16 + hercules_test/test_inputs/sum_int2.hir | 25 + rust-toolchain.toml | 2 + 25 files changed, 2251 insertions(+), 678 deletions(-) create mode 100644 hercules_samples/call.hir create mode 100644 hercules_test/hercules_interpreter/Cargo.toml create mode 100644 hercules_test/hercules_interpreter/src/interpreter.rs create mode 100644 hercules_test/hercules_interpreter/src/lib.rs create mode 100644 hercules_test/hercules_interpreter/src/main.rs create mode 100644 hercules_test/hercules_interpreter/src/value.rs create mode 100644 hercules_test/hercules_tests/Cargo.toml create mode 100644 hercules_test/hercules_tests/tests/opt_tests.rs create mode 100644 hercules_test/test_inputs/call.hir create mode 100644 hercules_test/test_inputs/call_dc_params.hir create mode 100644 hercules_test/test_inputs/ccp_example.hir create mode 100644 hercules_test/test_inputs/fork_join.hir create mode 100644 hercules_test/test_inputs/gvn_example.hir create mode 100644 hercules_test/test_inputs/matmul.hir create mode 100644 hercules_test/test_inputs/matmul_int.hir create mode 100644 hercules_test/test_inputs/simple1.hir create mode 100644 hercules_test/test_inputs/simple2.hir create mode 100644 hercules_test/test_inputs/simple3.hir create mode 100644 hercules_test/test_inputs/strset.hir create mode 100644 hercules_test/test_inputs/sum_int1.hir create mode 100644 hercules_test/test_inputs/sum_int2.hir create mode 100644 rust-toolchain.toml diff --git a/.gitignore b/.gitignore index 278d4690..feb7ba10 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ *.o *.hbin .*.swp +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 8248c425..c3138b1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,7 +47,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -66,148 +66,6 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.1.0", - "futures-lite 2.3.0", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.3.1", - "async-executor", - "async-io 2.3.3", - "async-lock 3.4.0", - "blocking", - "futures-lite 2.3.0", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" -dependencies = [ - "async-lock 3.4.0", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.3.0", - "parking", - "polling 3.7.1", - "rustix 0.38.34", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener 5.3.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 1.13.0", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "atomic-polyfill" version = "1.0.3" @@ -217,12 +75,6 @@ dependencies = [ "critical-section", ] -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.3.0" @@ -271,25 +123,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "blocking" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" -dependencies = [ - "async-channel 2.3.1", - "async-task", - "futures-io", - "futures-lite 2.3.0", - "piper", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "byteorder" version = "1.5.0" @@ -374,27 +207,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "critical-section" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - [[package]] name = "deranged" version = "0.3.11" @@ -405,68 +223,42 @@ dependencies = [ ] [[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" +name = "derive_more" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "libc", - "windows-sys 0.52.0", + "derive_more-impl", ] [[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.3.1" +name = "derive_more-impl" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "event-listener-strategy" -version = "0.5.2" +name = "either" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener 5.3.1", - "pin-project-lite", -] +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "fastrand" -version = "1.9.0" +name = "embedded-io" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" [[package]] -name = "fastrand" -version = "2.1.0" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "filetime" @@ -477,7 +269,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -492,55 +284,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand 2.1.0", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "getopts" version = "0.2.21" @@ -561,18 +304,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "hash32" version = "0.2.1" @@ -629,24 +360,28 @@ dependencies = [ ] [[package]] -name = "hercules_ir" +name = "hercules_interpreter" version = "0.1.0" dependencies = [ "bitvec", - "nom", + "clap", + "derive_more", + "hercules_ir", + "hercules_opt", + "itertools", "ordered-float", "rand", - "serde", ] [[package]] -name = "hercules_matmul" +name = "hercules_ir" version = "0.1.0" dependencies = [ - "async-std", - "clap", - "hercules_rt", + "bitvec", + "nom", + "ordered-float", "rand", + "serde", ] [[package]] @@ -682,20 +417,19 @@ dependencies = [ ] [[package]] -name = "hercules_task_parallel" +name = "hercules_tests" version = "0.1.0" dependencies = [ - "async-std", + "bitvec", "clap", - "hercules_rt", + "hercules_interpreter", + "hercules_ir", + "hercules_opt", + "itertools", + "ordered-float", + "rand", ] -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "indexmap" version = "2.2.6" @@ -707,46 +441,26 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.13" +name = "is_terminal_polyfill" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", + "either", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" - [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "juno_frontend" version = "0.1.0" @@ -763,15 +477,6 @@ dependencies = [ "phf", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -784,18 +489,6 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - [[package]] name = "lock_api" version = "0.4.12" @@ -806,15 +499,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" -dependencies = [ - "value-bag", -] - [[package]] name = "lrlex" version = "0.13.6" @@ -945,12 +629,6 @@ dependencies = [ "libc", ] -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - [[package]] name = "ordered-float" version = "4.2.0" @@ -970,12 +648,6 @@ dependencies = [ "serde", ] -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - [[package]] name = "phf" version = "0.11.2" @@ -1018,60 +690,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "piper" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" -dependencies = [ - "atomic-waker", - "fastrand 2.1.0", - "futures-io", -] - -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix 0.38.34", - "tracing", - "windows-sys 0.52.0", -] - [[package]] name = "postcard" version = "1.0.8" @@ -1209,33 +827,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", -] - [[package]] name = "rustversion" version = "1.0.17" @@ -1280,25 +871,6 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "sparsevec" version = "0.2.0" @@ -1394,22 +966,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -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" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -1428,12 +984,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "value-bag" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" - [[package]] name = "vergen" version = "8.3.1" @@ -1456,147 +1006,19 @@ dependencies = [ "serde", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -1605,46 +1027,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -1657,48 +1061,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/Cargo.toml b/Cargo.toml index c6af575e..14184efa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,14 @@ members = [ "hercules_rt", "hercules_rt_proc", + "hercules_test/hercules_interpreter", + "hercules_test/hercules_tests", + "hercules_tools/hercules_driver", "hercules_tools/hercules_hbin_dump", "juno_frontend", - "hercules_samples/matmul", - "hercules_samples/task_parallel" + #"hercules_samples/matmul", + #"hercules_samples/task_parallel" ] diff --git a/hercules_samples/call.hir b/hercules_samples/call.hir new file mode 100644 index 00000000..44748934 --- /dev/null +++ b/hercules_samples/call.hir @@ -0,0 +1,7 @@ +fn myfunc(x: i32) -> i32 + y = call(add, x, x) + r = return(start, y) + +fn add(x: i32, y: i32) -> i32 + w = add(x, y) + r = return(start, w) \ No newline at end of file diff --git a/hercules_test/hercules_interpreter/Cargo.toml b/hercules_test/hercules_interpreter/Cargo.toml new file mode 100644 index 00000000..d41caff8 --- /dev/null +++ b/hercules_test/hercules_interpreter/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "hercules_interpreter" +version = "0.1.0" +edition = "2021" + +[dependencies] +bitvec = "*" +clap = { version = "*", features = ["derive"] } +rand = "*" +hercules_ir = { path = "../../hercules_ir" } +hercules_opt = { path = "../../hercules_opt" } +itertools = "*" +ordered-float = "*" +derive_more = {version = "*", features = ["from"]} \ No newline at end of file diff --git a/hercules_test/hercules_interpreter/src/interpreter.rs b/hercules_test/hercules_interpreter/src/interpreter.rs new file mode 100644 index 00000000..a166427f --- /dev/null +++ b/hercules_test/hercules_interpreter/src/interpreter.rs @@ -0,0 +1,677 @@ +extern crate itertools; +extern crate ordered_float; + +use crate::value; + +use self::itertools::Itertools; +use std::collections::HashMap; +use std::panic; +use std::collections::hash_map::Entry::Occupied; + +use value::*; + +extern crate hercules_ir; +extern crate hercules_opt; + +use self::hercules_ir::*; + +/* High level design details / discussion for this: + * + * This crate includes tools for interpreting a hercules IR module. Execution model / flow is based on + * "A Simple Graph-Based Intermediate Representation" Section 3 by Cliff Click. + * + * In the future we would an *interactive* interpreter. I.e to be able to step forward and backwards + * throughout the IR (idealy visually) and analyze results at certain steps in the computation. + */ +pub struct FunctionExecutionState<'a> { + args: Vec<InterpreterVal>, // parameters + + // Map a vec of (thread indicies, reduce_node) -> Value + // Reduction values are shared across the fork / join pair that drives them, so they can't be local to a control token. + reduce_values: HashMap<(Vec<usize>, NodeID), InterpreterVal>, // Map (thread indicies, reduction node) -> value + join_counters: HashMap<(Vec<usize>, NodeID), usize>, // Map (thread indicies, join_node) -> join_counter. Counters until the joins are done! + dynamic_constant_params: Vec<usize>, + + // Per module State. + module: &'a Module, + function_contexts: &'a Vec<FunctionContext<'a>>, + + function_id: FunctionID, +} + +pub struct FunctionContext<'a> { + control_subgraph: &'a Subgraph, + def_use: &'a ImmutableDefUseMap, + fork_join_map: &'a HashMap<NodeID, NodeID>, // Map forks -> joins + fork_join_nest: &'a HashMap<NodeID, Vec<NodeID>>, +} + +impl <'a> FunctionContext<'a> { + pub fn new ( + control_subgraph: &'a Subgraph, + def_use: &'a ImmutableDefUseMap, + fork_join_map: &'a HashMap<NodeID, NodeID>, // Map forks -> joins + fork_join_nest: &'a HashMap<NodeID, Vec<NodeID>>, + ) -> FunctionContext<'a> { + FunctionContext { + control_subgraph, + def_use, + fork_join_map, + fork_join_nest, + } + } +} + +pub fn dyn_const_value(dc: &DynamicConstant, dyn_const_params: &[usize]) -> usize { + match dc { + DynamicConstant::Constant(v) => *v, + DynamicConstant::Parameter(v) => dyn_const_params[*v], + } +} +// Each control token stores a current position, and also a mapping of fork nodes -> thread idx. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ControlToken { + pub curr: NodeID, + pub prev: NodeID, // Used for properly latching PHI nodes, as they depend on their control predecessor. + pub thread_indicies: Vec<usize>, // Stack of thread_indicies, can use fork_join nests to recover thread indicies per fork. + + // Map phis -> values + // Oddly, these are stored per control token, when some other per-token data is stored in the FunctionExecutionState + // we could move this `phi_values` to be consistent, however that would make CoW behvaior optimization more difficult. + pub phi_values: HashMap<NodeID, InterpreterVal>, // TODO: Optimize this to a Cow<HashMap>, or optimize this whole thing. +} + +impl ControlToken { + pub fn moved_to(&self, next: NodeID) -> ControlToken { + ControlToken { curr: next, prev: self.curr, thread_indicies: self.thread_indicies.clone(), phi_values: self.phi_values.clone() } + } +} +impl<'a> FunctionExecutionState<'a> { + pub fn new( + args: Vec<InterpreterVal>, + module: &'a Module, + function_id: FunctionID, + function_contexts: &'a Vec<FunctionContext>, + dynamic_constant_params: Vec<usize>, + ) -> Self { + assert_eq!(args.len(), module.functions[function_id.idx()].param_types.len()); + + FunctionExecutionState { + args, + reduce_values: HashMap::new(), + join_counters: HashMap::new(), + module, + function_contexts, + function_id, + dynamic_constant_params, + } + } + + pub fn get_control_subgraph(&self) -> &Subgraph { + self.function_contexts[self.function_id.idx()].control_subgraph + } + + pub fn get_fork_join_map(&self) -> &HashMap<NodeID, NodeID> { + self.function_contexts[self.function_id.idx()].fork_join_map + } + + pub fn get_fork_join_nest(&self) -> &HashMap<NodeID, Vec<NodeID>> { + self.function_contexts[self.function_id.idx()].fork_join_nest + } + + pub fn get_def_use(&self) -> &ImmutableDefUseMap { + self.function_contexts[self.function_id.idx()].def_use + } + + pub fn get_function(&self) -> &Function { + &self.module.functions[self.function_id.idx()] + } + + /* Drives PHI values of this region for a control token, returns the next control node. */ + pub fn handle_region( + &mut self, + token: &mut ControlToken, + preds: &Box<[NodeID]>, + ) -> NodeID { + + let prev = token.prev; + let node = token.curr; + + // Gather PHI nodes for this region node. + let phis: Vec<NodeID> = self + .get_def_use() + .get_users(node) + .into_iter() + .filter_map(|n| { + if self.get_function().nodes[n.idx()].is_phi() { + Some(*n) + } else { + None + } + }) + .collect(); + + // Find incoming edge of this region. + let (edge_num, _) = preds + .into_iter() + .find_position(|pred| **pred == prev) + .expect("PANIC: invalid incoming edge."); + + let mut to_latch = vec![]; + + // Get values to latch at this point. + for phi in phis { + to_latch.push(self.handle_phi(token, edge_num, phi)); + } + + // Write values into map. + for (phi, value) in to_latch { + token.phi_values.insert(phi, value); + } + + // Return the (single) control successor of the region node + self.get_control_subgraph().succs(node).next().unwrap() + } + + pub fn handle_phi( + &mut self, + token: &ControlToken, + edge: usize, + phi: NodeID, + ) -> (NodeID, InterpreterVal) { + let (_control, data) = &self.get_function().nodes[phi.idx()] + .try_phi() + .expect("PANIC: handle_phi on non-phi node."); + let value_node = data[edge]; + // println!("Latching PHI value of node {:?}", value_node.idx()); + let value = self.handle_data(token, value_node); + (phi, value) + } + + // Drive all reductions for this join. + // TODO (@xrouth): Change all these to return vecs of ControlTokens, so this is consistent with other handle_* functions. + pub fn handle_join(&mut self, mut token: ControlToken, join: NodeID) -> Option<ControlToken> { + let reduces: Vec<NodeID> = self + .get_def_use() + .get_users(join) + .into_iter() + .filter_map(|n| { + if self.get_function().nodes[n.idx()].is_reduce() { + Some(*n) + } else { + None + } + }) + .collect(); + + for reduction in reduces { + self.handle_reduction(&token, reduction); + } + + let thread_values = self.get_thread_factors(&token, join); + // dbg!(thread_values.clone()); + // This and_modify doesn't do aynthing?? + self.join_counters + .entry((thread_values.clone(), join)) + .and_modify(|v| *v -= 1); + + if *self + .join_counters + .get(&(thread_values, join)) + .expect("PANIC: join counter not initialized") + == 0 + { + let curr = token.curr; + token.prev = curr; + token.thread_indicies.pop(); // Get rid of this thread index. + token.curr = self.get_control_subgraph().succs(join).next().unwrap(); + Some(token) + } else { + None + } + } + + /* Get top N-1 thread factors, where N is the depth of the control token. */ + pub fn get_thread_factors(&self, token: &ControlToken, control_node: NodeID) -> Vec<usize> { + let nested_forks = self + .get_fork_join_nest() + .get(&control_node) + .expect("PANIC: invalid node for fork join nest.") + .clone(); + // Take the top N entries such that it matches the length of the TRF in the control token. + + // Get the depth of the control token that is requesting this reduction node. + let fork_levels = nested_forks.len(); + + let len = fork_levels - 1; + let mut thread_values = token.thread_indicies.clone(); + thread_values.truncate(len); + thread_values + } + + pub fn intialize_reduction(&mut self, token_at_fork: &ControlToken, reduce: NodeID) { + + let token = token_at_fork; + + let (control, init, _) = &self.get_function().nodes[reduce.idx()] + .try_reduce() + .expect("PANIC: handle_reduction on non-reduce node."); + + let thread_values = self.get_thread_factors(token, *control); + + let init = self.handle_data(&token, *init); + // Q (@xrouth): It is UB to have the initializer depend on things within the fork-join section? do we check for that? + // A: Should be done in verify (TODO). + + self.reduce_values + .entry((thread_values.clone(), reduce)) + .or_insert(init); + } + + // Drive the reduction, this will be invoked for each control token. + pub fn handle_reduction(&mut self, token: &ControlToken, reduce: NodeID) { + let (control, _, reduct) = &self.get_function().nodes[reduce.idx()] + .try_reduce() + .expect("PANIC: handle_reduction on non-reduce node."); + + let thread_values = self.get_thread_factors(token, *control); + + // If empty set to default (figure out how to not repeat this check) + // TODO: (Can we do it upon entry to the fork node?) (YES!) + + let data = self.handle_data(&token, *reduct); + /* + println!( + "reduction write: {:?}, {:?}, {:?}", + thread_values, reduce, data + ); */ + + self.reduce_values.insert((thread_values, reduce), data); + } + + pub fn handle_data(&mut self, token: &ControlToken, node: NodeID) -> InterpreterVal { + // println!("Data Node: {} {:?}", node.idx(), &self.get_function().nodes[node.idx()]); + + // Partial borrow complaint. :/ + match &self.module.functions[self.function_id.idx()].nodes[node.idx()]{ + Node::Phi { control: _, data: _ } => (*token + .phi_values + .get(&node) + .expect("PANIC: Phi value not latched.")) + .clone(), + Node::ThreadID { control } => { + // `control` is the fork that drives this node. + let nesting_level = self + .get_fork_join_nest() + .get(control) + .expect("PANIC: No nesting information for thread index!") + .len(); + let v = token.thread_indicies[nesting_level - 1]; // Might have to -1? + InterpreterVal::DynamicConstant((v).into()) + } + // If we read from a reduction that is the same depth as this thread, we need to write back to it before anyone else reads from it. + // This probably isn't the exact condition, but somethign similar. Anyways, we achieve correctness by iterating control nodes recursively. + Node::Reduce { + control, + init, + reduct: _, + } => { + let thread_values = self.get_thread_factors(token, *control); + + // println!("reduction read: {:?}, {:?}", thread_values, node); + let entry = self + .reduce_values + .entry((thread_values, node)); + + let val = match entry { + Occupied(v) => v.get().clone(), + std::collections::hash_map::Entry::Vacant(_) => panic!("Reduce has not been initialized!"), + }; + // println!("value: {:?}", val.clone()); + val + } + Node::Parameter { index } => self.args[*index].clone(), + Node::Constant { id } => { + let con = &self.module.constants[id.idx()]; + InterpreterVal::from_constant( + con, + &self.module.constants, + &self.module.types, + &self.module.dynamic_constants, + &self.dynamic_constant_params + ) + } + Node::DynamicConstant { id } => { + let dyn_con = &self.module.dynamic_constants[id.idx()]; + let v = match dyn_con { + DynamicConstant::Constant(v) => v, + DynamicConstant::Parameter(v) => &self.dynamic_constant_params[*v], + }; + // TODO: Figure out what type / semantics are of thread ID and dynamic const. + InterpreterVal::DynamicConstant((*v).into()) + } + Node::Unary { input, op } => { + let val = self.handle_data(token, *input); + InterpreterVal::unary_op(*op, val) + } + Node::Binary { left, right, op } => { + let left = self.handle_data(token, *left); + let right = self.handle_data(token, *right); + + InterpreterVal::binary_op(*op, left, right) + } + Node::Ternary { + first, + second, + third, + op, + } => { + let first = self.handle_data(token, *first); + let second = self.handle_data(token, *second); + let third = self.handle_data(token, *third); + + match op { + TernaryOperator::Select => { + if first == InterpreterVal::Boolean(true) { + second + } else { + third + } + } + } + } + Node::Call { + function, + dynamic_constants, + args, + } => { + + let args = args.into_iter() + .map(|arg_node| self.handle_data(token, *arg_node)) + .collect(); + + + let dynamic_constant_params = dynamic_constants.into_iter() + .map(|id| { + let dyn_con = &self.module.dynamic_constants[id.idx()]; + let v = match dyn_con { + DynamicConstant::Constant(v) => *v, + DynamicConstant::Parameter(v) => self.dynamic_constant_params[*v], + }; + v + }).collect_vec(); + + let mut state = FunctionExecutionState::new( + args, + self.module, + *function, + self.function_contexts, + dynamic_constant_params, + ); + + state.run() + } + Node::Read { collect, indices } => { + let collection = self.handle_data(token, *collect); + + self.handle_read(token, collection, indices) + } + Node::Write { + collect, + data, + indices, + } => { + let collection = self.handle_data(token, *collect); + let data = self.handle_data(token, *data); + self.handle_write(token, collection, data, indices) + } + _ => todo!(), + } + } + + pub fn handle_write( + &mut self, + token: &ControlToken, + collection: InterpreterVal, + data: InterpreterVal, + indices: &[Index], + ) -> InterpreterVal { + let index = &indices[0]; + + // TODO (@xrouth): Recurse on writes correctly + let val = match index { + Index::Field(_) => todo!(), + Index::Variant(_) => todo!(), + Index::Position(array_indices) => { + // Arrays also have inner indices... + // Recover dimensional data from types. + let array_indices: Vec<_> = array_indices + .into_iter() + .map(|idx| self.handle_data(token, *idx).as_usize()) + .collect(); + // TODO: Implemenet . try_array() and other try_conversions on the InterpreterVal type + if let InterpreterVal::Array(type_id, mut vals) = collection { + // TODO: Make this its own funciton to reuse w/ array_size + let extents: Vec<_> = self.module.types[type_id.idx()] + .try_extents() + .expect("PANIC: wrong type for array") + .into_iter() + .map(|extent| dyn_const_value(&self.module.dynamic_constants[extent.idx()], &self.dynamic_constant_params)) + .collect(); + let idx = InterpreterVal::array_idx(&extents, &array_indices); + //println!("idx: {:?}", idx); + vals[idx] = data; + InterpreterVal::Array(type_id, vals) + } else { + panic!("PANIC: Position index on not an array") + } + } + }; + val + } + + pub fn handle_read( + &mut self, + token: &ControlToken, + collection: InterpreterVal, + indices: &[Index], + ) -> InterpreterVal { + let index = &indices[0]; + + let val = match index { + Index::Field(_) => todo!(), + Index::Variant(_) => todo!(), + Index::Position(array_indices) => { + // Arrays also have inner indices... + // Recover dimensional data from types. + let array_indices: Vec<_> = array_indices + .into_iter() + .map(|idx| self.handle_data(token, *idx).as_usize()) + .collect(); + + // TODO: Implemenet . try_array() and other try_conversions on the InterpreterVal type + if let InterpreterVal::Array(type_id, vals) = collection { + // TODO: Make this its own funciton to reuse w/ array_size + let extents: Vec<_> = self.module.types[type_id.idx()] + .try_extents() + .expect("PANIC: wrong type for array") + .into_iter() + .map(|extent| dyn_const_value(&self.module.dynamic_constants[extent.idx()], &self.dynamic_constant_params)) + .collect(); + vals[InterpreterVal::array_idx(&extents, &array_indices)].clone() + } else { + panic!("PANIC: Position index on not an array") + } + } + }; + + // Recurse + if indices.len() == 1 { + val + } else { + self.handle_read(token, val, &indices[1..]) + } + } + + pub fn run(&mut self) -> InterpreterVal { + let start_node: NodeID = NodeID::new(0); + + let start_token = ControlToken { + curr: start_node, + prev: start_node, + thread_indicies: Vec::new(), + phi_values: HashMap::new(), + }; + + let mut live_tokens: Vec<ControlToken> = Vec::new(); + live_tokens.push(start_token); + + + // To do reduction nodes correctly we have to traverse control tokens in a depth-first fashion (i.e immediately handle spawned threads). + 'outer: loop { + let mut ctrl_token = live_tokens.pop().expect("PANIC: Interpreter ran out of control tokens without returning."); + + /* println!( + "\n\nNew Token at: Control State: {} threads: {:?}, {:?}", + ctrl_token.curr.idx(), + ctrl_token.thread_indicies.clone(), + &self.get_function().nodes[ctrl_token.curr.idx()] + ); */ + + // TODO: Rust is annoying and can't recognize that this is a partial borrow. + // Can't partial borrow, so need a clone. + let node = &self.get_function().nodes[ctrl_token.curr.idx()].clone(); + let new_tokens = match node { + Node::Start => { + let next: NodeID = self.get_control_subgraph().succs(ctrl_token.curr).next().unwrap(); + + let ctrl_token = ctrl_token.moved_to(next); + + vec![ctrl_token] + } + Node::Region { preds } => { + + // Updates + let next = self.handle_region(&mut ctrl_token, &preds); + let ctrl_token = ctrl_token.moved_to(next); + + vec![ctrl_token] + } + Node::If { control: _, cond } => { + let cond = self.handle_data(&ctrl_token, *cond); + + // Convert condition to usize + let cond: usize = match cond { + InterpreterVal::Boolean(v) => v.into(), + _ => panic!("PANIC: Invalid condition for IF, please typecheck."), + }; + + // Find control read succesor that matches the condition. + let next = self + .get_control_subgraph() + .succs(ctrl_token.curr) + .find(|n| { + self.get_function().nodes[n.idx()] + .try_projection(cond) + .is_some() + }) + .expect("PANIC: No outgoing valid outgoing edge."); + + let ctrl_token = ctrl_token.moved_to(next); + vec![ctrl_token] + } + Node::Projection { .. } => { + let next: NodeID = self.get_control_subgraph().succs(ctrl_token.curr).next().unwrap(); + + let ctrl_token = ctrl_token.moved_to(next); + + vec![ctrl_token] + } + + Node::Match { control: _, sum: _ } => todo!(), + Node::Fork { control: _, factor } => { + let fork = ctrl_token.curr; + let dyn_con = &self.module.dynamic_constants[factor.idx()]; + + let thread_factor = match dyn_con { + DynamicConstant::Constant(v) => v, + DynamicConstant::Parameter(v) => &self.dynamic_constant_params[*v], + }.clone(); + + // Update control token + let next = self.get_control_subgraph().succs(ctrl_token.curr).nth(0).unwrap(); + let ctrl_token = ctrl_token.moved_to(next); + + let mut tokens_to_add = Vec::with_capacity(thread_factor); + + assert_ne!(thread_factor, 0); + + // Token is at its correct sontrol succesor already. + // Add the new thread index. + for i in 0..(thread_factor) { + let mut new_token = ctrl_token.clone(); // Copy map, curr, prev, etc. + new_token.thread_indicies.push(i); // Stack of thread indicies + tokens_to_add.push(new_token); + } + + let thread_factors = self.get_thread_factors(&ctrl_token, ctrl_token.curr); + + // Find join and initialize them, and set their reduction counters as well. + let join = self + .get_fork_join_map() + .get(&fork) + .expect("PANIC: fork missing a join.") + .clone(); + + let reduces: Vec<NodeID> = self + .get_def_use() + .get_users(join) + .into_iter() + .filter_map(|n| { + if self.get_function().nodes[n.idx()].is_reduce() { + Some(*n) + } else { + None + } + }) + .collect(); + + for reduction in reduces { + // TODO: Is this the correct reduction? + self.intialize_reduction(&ctrl_token, reduction); + } + + + self.join_counters.insert((thread_factors, join), thread_factor); + + tokens_to_add + } + Node::Join { control: _ } => { + let join = ctrl_token.curr; + + // Only make a control token if the join is finished, checked in handle_join. + if let Some(next) = self.handle_join(ctrl_token, join) { + vec![next] + } else { + vec![] + } + } + Node::Return { control: _, data } => { + let result = self.handle_data(&ctrl_token, *data); + // println!("result = {:?}", result); + break 'outer result; + } + _ => { + panic!("PANIC: Unexpected node in control subgraph {:?}", node); + } + }; + + for i in new_tokens { + live_tokens.push(i); + } + + } + } +} + + diff --git a/hercules_test/hercules_interpreter/src/lib.rs b/hercules_test/hercules_interpreter/src/lib.rs new file mode 100644 index 00000000..89fae51a --- /dev/null +++ b/hercules_test/hercules_interpreter/src/lib.rs @@ -0,0 +1,174 @@ +pub mod interpreter; +pub mod value; + +use std::fs::File; + +use hercules_ir::Module; +use hercules_ir::TypeID; + +pub use crate::interpreter::*; +pub use crate::value::*; + +// Get a vec of +pub fn into_interp_val(module: &Module, wrapper: InterpreterWrapper, target_ty_id: TypeID) -> InterpreterVal +{ + match wrapper { + InterpreterWrapper::Boolean(v) => InterpreterVal::Boolean(v), + InterpreterWrapper::Integer8(v) => InterpreterVal::Integer8(v), + InterpreterWrapper::Integer16(v) => InterpreterVal::Integer16(v), + InterpreterWrapper::Integer32(v) => InterpreterVal::Integer32(v), + InterpreterWrapper::Integer64(v) => InterpreterVal::Integer64(v), + InterpreterWrapper::UnsignedInteger8(v) => InterpreterVal::UnsignedInteger8(v), + InterpreterWrapper::UnsignedInteger16(v) => InterpreterVal::UnsignedInteger16(v), + InterpreterWrapper::UnsignedInteger32(v) => InterpreterVal::UnsignedInteger32(v), + InterpreterWrapper::UnsignedInteger64(v) => InterpreterVal::UnsignedInteger64(v), + InterpreterWrapper::Float32(v) => InterpreterVal::Float32(v), + InterpreterWrapper::Float64(v) => InterpreterVal::Float64(v), + InterpreterWrapper::Product(_) => todo!(), + InterpreterWrapper::Summation(_, _) => todo!(), + + InterpreterWrapper::Array(array) => { + let ty = &module.types[target_ty_id.idx()]; + let ele_type = ty.try_element_type().expect("PANIC: Type ID"); + // unwrap -> map to rust type, check + + let mut values = vec![]; + + for i in 0..array.len() { + values.push(into_interp_val(module, array[i].clone(), TypeID::new(0))); + } + + InterpreterVal::Array(target_ty_id, values.into_boxed_slice()) + } + } +} + +pub fn array_from_interp_val<T: Clone>(module: &Module, interp_val: InterpreterVal) -> Vec<T> + where value::InterpreterVal: Into<T> +{ + vec![] +} + +// Recursively turns rt args into interpreter wrappers. +#[macro_export] +macro_rules! parse_rt_args { + ($arg:expr) => { + { + + let mut values: Vec<InterpreterWrapper> = vec![]; + + values.push(($arg).into()); + + values + } + }; + ( $arg:expr, $($tail_args:expr), +) => { + { + let mut values: Vec<InterpreterWrapper> = vec![]; + + values.push($arg.into()); + + let mut recurse = parse_rt_args!($($tail_args), +); // How do you pass the rest? + values.append(&mut recurse); + values + } + }; +} + +pub fn parse_file(path: &str) -> Module { + let mut file = File::open(path).expect("PANIC: Unable to open input file."); + let mut contents = String::new(); + std::io::Read::read_to_string(&mut file, &mut contents) + .expect("PANIC: Unable to read input file contents."); + let module = + hercules_ir::parse::parse(&contents).expect("PANIC: Failed to parse Hercules IR file."); + module +} + +#[macro_export] +macro_rules! interp_module { + ($module:ident, $dynamic_constants:expr, $($args:expr), *) => { + { + //let hir_file = String::from($path); + + let wrappers = parse_rt_args!( $($args), *); + + let dynamic_constants: Vec<usize> = $dynamic_constants.into(); + let module = $module.clone(); //parse_file(hir_file); + + let mut pm = hercules_opt::pass::PassManager::new(module); + pm.add_pass(hercules_opt::pass::Pass::Verify); + + pm.run_passes(); + pm.make_reverse_postorders(); + pm.make_doms(); + pm.make_fork_join_maps(); + pm.make_fork_join_nests(); + pm.make_control_subgraphs(); + pm.make_plans(); + + let reverse_postorders = pm.reverse_postorders.as_ref().unwrap().clone(); + let doms = pm.doms.as_ref().unwrap().clone(); + let fork_join_maps = pm.fork_join_maps.as_ref().unwrap().clone(); + let fork_join_nests = pm.fork_join_nests.as_ref().unwrap().clone(); + let plans = pm.plans.as_ref().unwrap().clone(); + let control_subgraphs = pm.control_subgraphs.as_ref().unwrap().clone(); + let def_uses = pm.def_uses.as_ref().unwrap().clone(); + + let module = pm.get_module(); + + let mut function_contexts = vec![]; + + for idx in 0..module.functions.len() { + let context = FunctionContext::new( + &control_subgraphs[idx], + &def_uses[idx], + &fork_join_maps[idx], + &fork_join_nests[idx], + ); + function_contexts.push(context); + } + + let function_number = 0; + + let parameter_types = &module.functions[function_number].param_types; + + let args = wrappers.into_iter().enumerate().map(|(idx, val)| {into_interp_val(&module, val, parameter_types[idx])}).collect(); + + let mut state = FunctionExecutionState::new( + args, + &module, + hercules_ir::FunctionID::new(function_number), + &function_contexts, + dynamic_constants, + ); + + state.run() + } + }; +} + + +#[macro_export] +macro_rules! interp_file_with_passes { + ($path:literal, $dynamic_constants:expr, $passes:expr, $($args:expr), *) => { + { + let module = parse_file($path); + + let result_before = interp_module!(module, $dynamic_constants, $($args), *); + + let mut pm = hercules_opt::pass::PassManager::new(module.clone()); + + for pass in $passes { + pm.add_pass(pass); + } + + pm.run_passes(); + + let module = pm.get_module(); + let result_after = interp_module!(module, $dynamic_constants, $($args), *); + + assert_eq!(result_after, result_before); + } + }; +} \ No newline at end of file diff --git a/hercules_test/hercules_interpreter/src/main.rs b/hercules_test/hercules_interpreter/src/main.rs new file mode 100644 index 00000000..4d2ebd31 --- /dev/null +++ b/hercules_test/hercules_interpreter/src/main.rs @@ -0,0 +1,33 @@ +extern crate clap; +extern crate hercules_ir; +extern crate hercules_opt; +extern crate rand; + + +use hercules_interpreter::interpreter::*; +use hercules_interpreter::*; +use hercules_interpreter::value; +use std::fs::File; +use std::io::prelude::*; + +use self::hercules_ir::*; + +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + hir_file: String, + + #[arg(short, long, default_value_t = String::new())] + output: String, +} + +fn main() { + let args = Args::parse(); + let module = parse_file(&args.hir_file); + let ret_val = interp_module!(module, [2, 3, 4], 1, 3); + + println!("ret val: {:?}", ret_val); +} + diff --git a/hercules_test/hercules_interpreter/src/value.rs b/hercules_test/hercules_interpreter/src/value.rs new file mode 100644 index 00000000..1c52fea8 --- /dev/null +++ b/hercules_test/hercules_interpreter/src/value.rs @@ -0,0 +1,852 @@ +#![allow(unused)] + +extern crate derive_more; +use derive_more::{From}; + +/* Defines semantic meaning of IR operations. */ +extern crate itertools; + +use crate::dyn_const_value; + +use self::itertools::Itertools; +use std::clone; +use std::convert::TryInto; +use std::panic; + +extern crate hercules_ir; +extern crate hercules_opt; + +use self::hercules_ir::*; +use self::hercules_opt::*; + +#[derive(PartialEq, Debug, Clone, Eq)] +pub enum InterpreterVal { + Boolean(bool), + Integer8(i8), + Integer16(i16), + Integer32(i32), + Integer64(i64), + UnsignedInteger8(u8), + UnsignedInteger16(u16), + UnsignedInteger32(u32), + UnsignedInteger64(u64), + + Float32(ordered_float::OrderedFloat<f32>), + Float64(ordered_float::OrderedFloat<f64>), + + Product(TypeID, Box<[InterpreterVal]>), + Summation(TypeID, u32, Box<[InterpreterVal]>), + Array(TypeID, Box<[InterpreterVal]>), // TypeID of the array Type (not the element type) + // These can be freely? casted + DynamicConstant(usize), + ThreadID(usize), +} + +#[derive(Clone)] +pub enum InterpreterWrapper { + Boolean(bool), + Integer8(i8), + Integer16(i16), + Integer32(i32), + Integer64(i64), + UnsignedInteger8(u8), + UnsignedInteger16(u16), + UnsignedInteger32(u32), + UnsignedInteger64(u64), + + Float32(ordered_float::OrderedFloat<f32>), + Float64(ordered_float::OrderedFloat<f64>), + + Product(Box<[InterpreterWrapper]>), + Summation(u32, Box<[InterpreterWrapper]>), + Array(Box<[InterpreterWrapper]>), // TypeID of the array Type (not the element type) +} + + + +impl <T> From<&Vec<T>> for InterpreterWrapper + where T: Into<InterpreterWrapper> + Clone +{ + + fn from(value: &Vec<T>) -> Self { + let mut values = vec![]; + for i in 0..value.len() { + values[i] = value[i].clone().into() + } + InterpreterWrapper::Array(values.into_boxed_slice()) + } +} + +impl <T> From<Vec<T>> for InterpreterWrapper + where T: Into<InterpreterWrapper> + Clone +{ + fn from(value: Vec<T>) -> Self { + let mut values = vec![]; + for i in 0..value.len() { + values.push(value[i].clone().into()); + } + InterpreterWrapper::Array(values.into_boxed_slice()) + } +} + + +impl <T> From<&[T]> for InterpreterWrapper + where T: Into<InterpreterWrapper> + Clone +{ + + fn from(value: &[T]) -> Self { + let mut values = vec![]; + for i in 0..value.len() { + values[i] = value[i].clone().into() + } + InterpreterWrapper::Array(values.into_boxed_slice()) + } +} + +// Map rust types to interpreter values. +macro_rules! from_impl { + ($rust:ty, $variant:tt) => { + impl From<$rust> for InterpreterWrapper { + fn from(value: $rust) -> Self { + InterpreterWrapper::$variant(value) + } + } + }; +} + +from_impl!(bool, Boolean); +from_impl!(i8, Integer8); +from_impl!(i16, Integer16); +from_impl!(i32, Integer32); +from_impl!(i64, Integer64); +from_impl!(u8, UnsignedInteger8); +from_impl!(u16, UnsignedInteger16); +from_impl!(u32, UnsignedInteger32); +from_impl!(u64, UnsignedInteger64); + +impl<'a> InterpreterVal { + pub fn from_constant( + constant: &Constant, + constants: &'a Vec<Constant>, + types: &'a Vec<Type>, + dynamic_constants: &'a [DynamicConstant], + dynamic_constant_params: &'a [usize], + ) -> Self { + match *constant { + Constant::Boolean(v) => Self::Boolean(v), + Constant::Integer8(v) => Self::Integer8(v), + Constant::Integer16(v) => Self::Integer16(v), + Constant::Integer32(v) => Self::Integer32(v), + Constant::Integer64(v) => Self::Integer64(v), + Constant::UnsignedInteger8(v) => Self::UnsignedInteger8(v), + Constant::UnsignedInteger16(v) => Self::UnsignedInteger16(v), + Constant::UnsignedInteger32(v) => Self::UnsignedInteger32(v), + Constant::UnsignedInteger64(v) => Self::UnsignedInteger64(v), + Constant::Float32(v) => Self::Float32(v), + Constant::Float64(v) => Self::Float64(v), + + Constant::Product(_, _) => todo!(), + Constant::Summation(_, _, _) => todo!(), + Constant::Array(type_id) => { + // TODO: This is currently only implemented for arrays of primitive types, implement zero initializers for other types. + let ty = &types[type_id.idx()]; + + let extents: Vec<_> = ty + .try_extents() + .expect("PANIC: wrong type for array") + .into_iter() + .map(|extent| dyn_const_value(&dynamic_constants[extent.idx()], &dynamic_constant_params)) + .collect(); + + let size = InterpreterVal::array_size(&extents); + + // Need to recurse here, ugh. + let element_type_id = ty.try_element_type().expect("PANIC: no element type"); + let element_type = &types[element_type_id.idx()]; + + // TODO Need a seperate funciton to handle this, as arrays w/ non-primitive elements should use Constant::Zero(element_id), + // and arrays w/ primitive elements should use the zero-initializer for those. + + let element_zeroed = InterpreterVal::zero_initialized(&element_type); + let backing_array = vec![element_zeroed; size]; + Self::Array(type_id, backing_array.into_boxed_slice()) + } + } + } + + /* Zero initialized interpreter value of a hercules type, only supported for primitive types. */ + pub fn zero_initialized(ty: &'a Type) -> InterpreterVal { + match ty { + Type::Boolean => Self::Boolean(false), + Type::Integer8 => Self::Integer8(0), + Type::Integer16 => Self::Integer16(0), + Type::Integer32 => Self::Integer32(0), + Type::Integer64 => Self::Integer64(0), + Type::UnsignedInteger8 => Self::UnsignedInteger8(0), + Type::UnsignedInteger16 => Self::UnsignedInteger16(0), + Type::UnsignedInteger32 => Self::UnsignedInteger32(0), + Type::UnsignedInteger64 => Self::UnsignedInteger64(0), + //Type::Float32 => Self::Float32(0.0), + //Type::Float64 => Self::Float64(0.0), + _ => todo!("Zero initializer not implemented yet for this type"), + } + } + + // TODO: Find some way to reuse this code and conditional constant prop code. + pub fn binary_op( + op: BinaryOperator, + left: InterpreterVal, + right: InterpreterVal, + ) -> InterpreterVal { + + // Do some type conversion first. + let left = match left { + InterpreterVal::DynamicConstant(v) => match right { + InterpreterVal::Integer8(_) => InterpreterVal::Integer8(v.try_into().unwrap()), + InterpreterVal::Integer16(_) => InterpreterVal::Integer16(v.try_into().unwrap()), + InterpreterVal::Integer32(_) => InterpreterVal::Integer32(v.try_into().unwrap()), + InterpreterVal::Integer64(_) => InterpreterVal::Integer64(v.try_into().unwrap()), + InterpreterVal::UnsignedInteger8(_) => { + InterpreterVal::UnsignedInteger8(v.try_into().unwrap()) + } + InterpreterVal::UnsignedInteger16(_) => { + InterpreterVal::UnsignedInteger16(v.try_into().unwrap()) + } + InterpreterVal::UnsignedInteger32(_) => { + InterpreterVal::UnsignedInteger64(v.try_into().unwrap()) + } + InterpreterVal::UnsignedInteger64(_) => { + InterpreterVal::UnsignedInteger64(v.try_into().unwrap()) + } + InterpreterVal::DynamicConstant(_) => { + panic!("PANIC: Some math on dynamic constants is unimplemented") + } + // InterpreterVal::ThreadID(_) => InterpreterVal::Boolean(v), + _ => panic!("PANIC: Some math on dynamic constants is unimplemented"), + }, + // InterpreterVal::ThreadID(v) => todo!(), + _ => left, + }; + + let right = match right { + InterpreterVal::DynamicConstant(v) => match left { + InterpreterVal::Integer8(_) => InterpreterVal::Integer8(v.try_into().unwrap()), + InterpreterVal::Integer16(_) => InterpreterVal::Integer16(v.try_into().unwrap()), + InterpreterVal::Integer32(_) => InterpreterVal::Integer32(v.try_into().unwrap()), + InterpreterVal::Integer64(_) => InterpreterVal::Integer64(v.try_into().unwrap()), + InterpreterVal::UnsignedInteger8(_) => { + InterpreterVal::UnsignedInteger8(v.try_into().unwrap()) + } + InterpreterVal::UnsignedInteger16(_) => { + InterpreterVal::UnsignedInteger16(v.try_into().unwrap()) + } + InterpreterVal::UnsignedInteger32(_) => { + InterpreterVal::UnsignedInteger32(v.try_into().unwrap()) + } + InterpreterVal::UnsignedInteger64(_) => { + InterpreterVal::UnsignedInteger64(v.try_into().unwrap()) + } + InterpreterVal::DynamicConstant(_) => { + panic!("PANIC: Some math on dynamic constants is unimplemented") + } + _ => panic!("PANIC: Some math on dynamic constants is unimplemented"), + }, + // InterpreterVal::ThreadID(v) => todo!(), + _ => right, + }; + + match (op, left.clone(), right.clone()) { + (BinaryOperator::Add, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val + right_val) + } + (BinaryOperator::Add, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val + right_val) + } + (BinaryOperator::Add, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val + right_val) + } + (BinaryOperator::Add, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val + right_val) + } + ( + BinaryOperator::Add, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val + right_val), + ( + BinaryOperator::Add, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val + right_val), + ( + BinaryOperator::Add, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val + right_val), + ( + BinaryOperator::Add, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val + right_val), + (BinaryOperator::Add, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Float32(left_val + right_val) + } + (BinaryOperator::Add, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Float64(left_val + right_val) + } + (BinaryOperator::Sub, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val - right_val) + } + (BinaryOperator::Sub, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val - right_val) + } + (BinaryOperator::Sub, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val - right_val) + } + (BinaryOperator::Sub, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val - right_val) + } + ( + BinaryOperator::Sub, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val - right_val), + ( + BinaryOperator::Sub, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val - right_val), + ( + BinaryOperator::Sub, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val - right_val), + ( + BinaryOperator::Sub, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val - right_val), + (BinaryOperator::Sub, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Float32(left_val - right_val) + } + (BinaryOperator::Sub, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Float64(left_val - right_val) + } + (BinaryOperator::Mul, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val * right_val) + } + (BinaryOperator::Mul, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val * right_val) + } + (BinaryOperator::Mul, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val * right_val) + } + (BinaryOperator::Mul, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val * right_val) + } + ( + BinaryOperator::Mul, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val * right_val), + ( + BinaryOperator::Mul, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val * right_val), + ( + BinaryOperator::Mul, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val * right_val), + ( + BinaryOperator::Mul, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val * right_val), + (BinaryOperator::Mul, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Float32(left_val * right_val) + } + (BinaryOperator::Mul, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Float64(left_val * right_val) + } + (BinaryOperator::Div, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val / right_val) + } + (BinaryOperator::Div, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val / right_val) + } + (BinaryOperator::Div, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val / right_val) + } + (BinaryOperator::Div, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val / right_val) + } + ( + BinaryOperator::Div, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val / right_val), + ( + BinaryOperator::Div, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val / right_val), + ( + BinaryOperator::Div, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val / right_val), + ( + BinaryOperator::Div, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val / right_val), + (BinaryOperator::Div, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Float32(left_val / right_val) + } + (BinaryOperator::Div, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Float64(left_val / right_val) + } + (BinaryOperator::Rem, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val % right_val) + } + (BinaryOperator::Rem, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val % right_val) + } + (BinaryOperator::Rem, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val % right_val) + } + (BinaryOperator::Rem, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val % right_val) + } + ( + BinaryOperator::Rem, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val % right_val), + ( + BinaryOperator::Rem, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val % right_val), + ( + BinaryOperator::Rem, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val % right_val), + ( + BinaryOperator::Rem, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val % right_val), + (BinaryOperator::Rem, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Float32(left_val % right_val) + } + (BinaryOperator::Rem, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Float64(left_val % right_val) + } + (BinaryOperator::LT, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Boolean(left_val < right_val) + } + (BinaryOperator::LT, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Boolean(left_val < right_val) + } + (BinaryOperator::LT, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Boolean(left_val < right_val) + } + (BinaryOperator::LT, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Boolean(left_val < right_val) + } + ( + BinaryOperator::LT, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::Boolean(left_val < right_val), + ( + BinaryOperator::LT, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::Boolean(left_val < right_val), + ( + BinaryOperator::LT, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::Boolean(left_val < right_val), + ( + BinaryOperator::LT, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::Boolean(left_val < right_val), + (BinaryOperator::LT, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Boolean(*left_val < *right_val) + } + (BinaryOperator::LT, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Boolean(*left_val < *right_val) + } + (BinaryOperator::LTE, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Boolean(left_val <= right_val) + } + (BinaryOperator::LTE, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Boolean(left_val <= right_val) + } + (BinaryOperator::LTE, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Boolean(left_val <= right_val) + } + (BinaryOperator::LTE, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Boolean(left_val <= right_val) + } + ( + BinaryOperator::LTE, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::Boolean(left_val <= right_val), + ( + BinaryOperator::LTE, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::Boolean(left_val <= right_val), + ( + BinaryOperator::LTE, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::Boolean(left_val <= right_val), + ( + BinaryOperator::LTE, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::Boolean(left_val <= right_val), + (BinaryOperator::LTE, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Boolean(*left_val <= *right_val) + } + (BinaryOperator::LTE, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Boolean(*left_val <= *right_val) + } + (BinaryOperator::GT, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Boolean(left_val > right_val) + } + (BinaryOperator::GT, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Boolean(left_val > right_val) + } + (BinaryOperator::GT, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Boolean(left_val > right_val) + } + (BinaryOperator::GT, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Boolean(left_val > right_val) + } + ( + BinaryOperator::GT, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::Boolean(left_val > right_val), + ( + BinaryOperator::GT, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::Boolean(left_val > right_val), + ( + BinaryOperator::GT, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::Boolean(left_val > right_val), + ( + BinaryOperator::GT, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::Boolean(left_val > right_val), + (BinaryOperator::GT, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Boolean(*left_val > *right_val) + } + (BinaryOperator::GT, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Boolean(*left_val > *right_val) + } + (BinaryOperator::GTE, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Boolean(left_val >= right_val) + } + (BinaryOperator::GTE, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Boolean(left_val >= right_val) + } + (BinaryOperator::GTE, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Boolean(left_val >= right_val) + } + (BinaryOperator::GTE, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Boolean(left_val >= right_val) + } + ( + BinaryOperator::GTE, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::Boolean(left_val >= right_val), + ( + BinaryOperator::GTE, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::Boolean(left_val >= right_val), + ( + BinaryOperator::GTE, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::Boolean(left_val >= right_val), + ( + BinaryOperator::GTE, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::Boolean(left_val >= right_val), + (BinaryOperator::GTE, Self::Float32(left_val), Self::Float32(right_val)) => { + Self::Boolean(*left_val >= *right_val) + } + (BinaryOperator::GTE, Self::Float64(left_val), Self::Float64(right_val)) => { + Self::Boolean(*left_val >= *right_val) + } + // EQ and NE can be implemented more easily, since we don't + // need to unpack the left and right values. + (BinaryOperator::EQ, left_val, right_val) => Self::Boolean(left_val == right_val), + (BinaryOperator::NE, left_val, right_val) => Self::Boolean(left_val != right_val), + (BinaryOperator::Or, Self::Boolean(left_val), Self::Boolean(right_val)) => { + Self::Boolean(left_val || right_val) + } + (BinaryOperator::Or, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val | right_val) + } + (BinaryOperator::Or, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val | right_val) + } + (BinaryOperator::Or, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val | right_val) + } + (BinaryOperator::Or, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val | right_val) + } + ( + BinaryOperator::Or, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val | right_val), + ( + BinaryOperator::Or, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val | right_val), + ( + BinaryOperator::Or, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val | right_val), + ( + BinaryOperator::Or, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val | right_val), + (BinaryOperator::And, Self::Boolean(left_val), Self::Boolean(right_val)) => { + Self::Boolean(left_val && right_val) + } + (BinaryOperator::And, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val & right_val) + } + (BinaryOperator::And, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val & right_val) + } + (BinaryOperator::And, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val & right_val) + } + (BinaryOperator::And, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val & right_val) + } + ( + BinaryOperator::And, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val & right_val), + ( + BinaryOperator::And, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val & right_val), + ( + BinaryOperator::And, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val & right_val), + ( + BinaryOperator::And, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val & right_val), + (BinaryOperator::Xor, Self::Boolean(left_val), Self::Boolean(right_val)) => { + Self::Boolean(left_val ^ right_val) + } + (BinaryOperator::Xor, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val ^ right_val) + } + (BinaryOperator::Xor, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val ^ right_val) + } + (BinaryOperator::Xor, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val ^ right_val) + } + (BinaryOperator::Xor, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val ^ right_val) + } + ( + BinaryOperator::Xor, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val ^ right_val), + ( + BinaryOperator::Xor, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val ^ right_val), + ( + BinaryOperator::Xor, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val ^ right_val), + ( + BinaryOperator::Xor, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val ^ right_val), + (BinaryOperator::LSh, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val << right_val) + } + (BinaryOperator::LSh, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val << right_val) + } + (BinaryOperator::LSh, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val << right_val) + } + (BinaryOperator::LSh, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val << right_val) + } + ( + BinaryOperator::LSh, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val << right_val), + ( + BinaryOperator::LSh, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val << right_val), + ( + BinaryOperator::LSh, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val << right_val), + ( + BinaryOperator::LSh, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val << right_val), + (BinaryOperator::RSh, Self::Integer8(left_val), Self::Integer8(right_val)) => { + Self::Integer8(left_val >> right_val) + } + (BinaryOperator::RSh, Self::Integer16(left_val), Self::Integer16(right_val)) => { + Self::Integer16(left_val >> right_val) + } + (BinaryOperator::RSh, Self::Integer32(left_val), Self::Integer32(right_val)) => { + Self::Integer32(left_val >> right_val) + } + (BinaryOperator::RSh, Self::Integer64(left_val), Self::Integer64(right_val)) => { + Self::Integer64(left_val >> right_val) + } + ( + BinaryOperator::RSh, + Self::UnsignedInteger8(left_val), + Self::UnsignedInteger8(right_val), + ) => Self::UnsignedInteger8(left_val >> right_val), + ( + BinaryOperator::RSh, + Self::UnsignedInteger16(left_val), + Self::UnsignedInteger16(right_val), + ) => Self::UnsignedInteger16(left_val >> right_val), + ( + BinaryOperator::RSh, + Self::UnsignedInteger32(left_val), + Self::UnsignedInteger32(right_val), + ) => Self::UnsignedInteger32(left_val >> right_val), + ( + BinaryOperator::RSh, + Self::UnsignedInteger64(left_val), + Self::UnsignedInteger64(right_val), + ) => Self::UnsignedInteger64(left_val >> right_val), + _ => { + println!("{:?}, {:?}", left, right); + panic!("Unsupported combination of binary operation and constant values. Did typechecking succeed?") + } + } + } + + pub fn unary_op(op: UnaryOperator, val: InterpreterVal) -> Self { + match (op, val) { + (UnaryOperator::Not, Self::Boolean(val)) => Self::Boolean(!val), + (UnaryOperator::Not, Self::Integer8(val)) => Self::Integer8(!val), + (UnaryOperator::Not, Self::Integer16(val)) => Self::Integer16(!val), + (UnaryOperator::Not, Self::Integer32(val)) => Self::Integer32(!val), + (UnaryOperator::Not, Self::Integer64(val)) => Self::Integer64(!val), + (UnaryOperator::Neg, Self::Integer8(val)) => Self::Integer8(-val), + (UnaryOperator::Neg, Self::Integer16(val)) => Self::Integer16(-val), + (UnaryOperator::Neg, Self::Integer32(val)) => Self::Integer32(-val), + (UnaryOperator::Neg, Self::Integer64(val)) => Self::Integer64(-val), + (UnaryOperator::Neg, Self::Float32(val)) => Self::Float32(-val), + (UnaryOperator::Neg, Self::Float64(val)) => Self::Float64(-val), + (UnaryOperator::Cast(_), _) => todo!("Write cast impl"), + _ => panic!("Unsupported combination of unary operation and constant value. Did typechecking succeed?") + } + } + + pub fn as_usize(&self) -> usize { + match *self { + InterpreterVal::Boolean(v) => v.try_into().unwrap(), + InterpreterVal::Integer8(v) => v.try_into().unwrap(), + InterpreterVal::Integer16(v) => v.try_into().unwrap(), + InterpreterVal::Integer32(v) => v.try_into().unwrap(), + InterpreterVal::Integer64(v) => v.try_into().unwrap(), + InterpreterVal::UnsignedInteger8(v) => v.try_into().unwrap(), + InterpreterVal::UnsignedInteger16(v) => v.try_into().unwrap(), + InterpreterVal::UnsignedInteger32(v) => v.try_into().unwrap(), + InterpreterVal::UnsignedInteger64(v) => v.try_into().unwrap(), + InterpreterVal::DynamicConstant(v) => v, + InterpreterVal::ThreadID(v) => v, + _ => panic!("PANIC: Value not castable to usize"), + } + } + + // Defines row major / how we layout our arrays + pub fn array_idx(extents: &[usize], indices: &[usize]) -> usize { + let a = extents + .into_iter() + .enumerate() + .map(|(i, n)| { + extents[i + 1..] + .into_iter() + .cloned() + .reduce(|a, b| a * b) + .unwrap_or(1) + * indices[i] + }) + .sum(); + a + } + + pub fn array_size(extents: &[usize]) -> usize { + extents + .into_iter() + .cloned() + .reduce(|a, b| a * b) + .unwrap_or(1) + } +} + +mod tests { + use super::*; + + #[test] + fn test_array_idx() { + let extents = [10, 5]; + let indices = [1, 1]; + let idx = InterpreterVal::array_idx(&extents, &indices); + assert_eq!(idx, 6) + } +} diff --git a/hercules_test/hercules_tests/Cargo.toml b/hercules_test/hercules_tests/Cargo.toml new file mode 100644 index 00000000..9bd6fe7b --- /dev/null +++ b/hercules_test/hercules_tests/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "hercules_tests" +version = "0.1.0" +edition = "2021" + +[dependencies] +bitvec = "*" +clap = { version = "*", features = ["derive"] } +rand = "*" +hercules_ir = { path = "../../hercules_ir" } +hercules_opt = { path = "../../hercules_opt" } +hercules_interpreter = { path = "../hercules_interpreter" } +itertools = "*" +ordered-float = "*" diff --git a/hercules_test/hercules_tests/tests/opt_tests.rs b/hercules_test/hercules_tests/tests/opt_tests.rs new file mode 100644 index 00000000..c14d4db5 --- /dev/null +++ b/hercules_test/hercules_tests/tests/opt_tests.rs @@ -0,0 +1,201 @@ +use std::env; + +use hercules_interpreter::*; +use hercules_opt::pass::Pass; + +extern crate rand; +use rand::Rng; + +#[test] +fn matmul_int() { + let module = parse_file("../test_inputs/matmul_int.hir"); + let dyn_consts = [2, 2, 2]; + let m1 = vec![3, 4, 5, 6]; + let m2 = vec![7, 8, 9, 10]; + let result_1 = interp_module!(module, dyn_consts, m1.clone(), m2.clone()); + + let mut pm = hercules_opt::pass::PassManager::new(module.clone()); + + let passes = vec![ + Pass::Verify, + Pass::CCP, + Pass::DCE, + Pass::GVN, + Pass::DCE, + Pass::Forkify, + Pass::DCE, + Pass::Predication, + Pass::DCE, + ]; + + for pass in passes { + pm.add_pass(pass); + } + pm.run_passes(); + + let module = pm.get_module(); + let result_2 = interp_module!(module, dyn_consts, m1, m2); + assert_eq!(result_1, result_2) +} + +#[test] +fn ccp_example() { + let module = parse_file("../test_inputs/ccp_example.hir"); + let dyn_consts = []; + let x = 34; + let result_1 = interp_module!(module, dyn_consts, x); + + let mut pm = hercules_opt::pass::PassManager::new(module.clone()); + + let passes = vec![ + Pass::Verify, + Pass::CCP, + Pass::DCE, + Pass::GVN, + Pass::DCE, + Pass::Forkify, + Pass::DCE, + Pass::Predication, + Pass::DCE, + ]; + + for pass in passes { + pm.add_pass(pass); + } + pm.run_passes(); + + let module = pm.get_module(); + let result_2 = interp_module!(module, dyn_consts, x); + assert_eq!(result_1, result_2) +} + +#[test] +fn gvn_example() { + let module = parse_file("../test_inputs/gvn_example.hir"); + + let dyn_consts = []; + let x: i32 = rand::random(); + let x = x / 32; + let y: i32 = rand::random(); + let y = y / 32; // prevent overflow, + let result_1 = interp_module!(module, dyn_consts, x, y); + + let mut pm = hercules_opt::pass::PassManager::new(module.clone()); + + let passes = vec![ + Pass::Verify, + Pass::CCP, + Pass::DCE, + Pass::GVN, + Pass::DCE, + Pass::Forkify, + Pass::DCE, + Pass::Predication, + Pass::DCE, + ]; + + for pass in passes { + pm.add_pass(pass); + } + pm.run_passes(); + + let module = pm.get_module(); + let result_2 = interp_module!(module, dyn_consts, x, y); + assert_eq!(result_1, result_2) +} + +#[test] +fn sum_int() { + let module = parse_file("../test_inputs/sum_int1.hir"); + + let size = 100; + let dyn_consts = [size]; + let mut vec = vec![0; size]; + let mut rng = rand::thread_rng(); + + for x in vec.iter_mut() { + *x = rng.gen::<i32>() / 100; + } + + let result_1 = interp_module!(module, dyn_consts, vec.clone()); + + let mut pm = hercules_opt::pass::PassManager::new(module.clone()); + + let passes = vec![ + Pass::Verify, + Pass::CCP, + Pass::DCE, + Pass::GVN, + Pass::DCE, + Pass::Forkify, + Pass::DCE, + Pass::Predication, + Pass::DCE, + ]; + + for pass in passes { + pm.add_pass(pass); + } + pm.run_passes(); + + let module = pm.get_module(); + let result_2 = interp_module!(module, dyn_consts, vec); + assert_eq!(result_1, result_2) +} + +#[test] +fn sum_int2() { + let module = parse_file("../test_inputs/sum_int2.hir"); + + let size = 100; + let dyn_consts = [size]; + let mut vec = vec![0; size]; + let mut rng = rand::thread_rng(); + + for x in vec.iter_mut() { + *x = rng.gen::<i32>() / 100; + } + + let result_1 = interp_module!(module, dyn_consts, vec.clone()); + + let mut pm = hercules_opt::pass::PassManager::new(module.clone()); + + let passes = vec![ + Pass::Verify, + Pass::CCP, + Pass::DCE, + Pass::GVN, + Pass::DCE, + Pass::Forkify, + Pass::DCE, + Pass::Predication, + Pass::DCE, + ]; + + for pass in passes { + pm.add_pass(pass); + } + pm.run_passes(); + + let module = pm.get_module(); + let result_2 = interp_module!(module, dyn_consts, vec); + assert_eq!(result_1, result_2) +} + +#[test] +fn sum_int2_smaller() { + interp_file_with_passes!("../test_inputs/sum_int2.hir", + [100], + vec![ + Pass::Verify, + Pass::CCP, + Pass::DCE, + Pass::GVN, + Pass::DCE, + Pass::Forkify, + Pass::DCE, + Pass::Predication, + Pass::DCE, + ], + vec![1; 100]); +} \ No newline at end of file diff --git a/hercules_test/test_inputs/call.hir b/hercules_test/test_inputs/call.hir new file mode 100644 index 00000000..44748934 --- /dev/null +++ b/hercules_test/test_inputs/call.hir @@ -0,0 +1,7 @@ +fn myfunc(x: i32) -> i32 + y = call(add, x, x) + r = return(start, y) + +fn add(x: i32, y: i32) -> i32 + w = add(x, y) + r = return(start, w) \ No newline at end of file diff --git a/hercules_test/test_inputs/call_dc_params.hir b/hercules_test/test_inputs/call_dc_params.hir new file mode 100644 index 00000000..5ccf2686 --- /dev/null +++ b/hercules_test/test_inputs/call_dc_params.hir @@ -0,0 +1,9 @@ +fn myfunc(x: u64) -> u64 + y = call<10, 4>(add, x, x) + r = return(start, y) + +fn add<2>(x: u64, y: u64) -> u64 + b = dynamic_constant(#1) + r = return(start, z) + w = add(x, y) + z = add(b, w) \ No newline at end of file diff --git a/hercules_test/test_inputs/ccp_example.hir b/hercules_test/test_inputs/ccp_example.hir new file mode 100644 index 00000000..25b7379e --- /dev/null +++ b/hercules_test/test_inputs/ccp_example.hir @@ -0,0 +1,19 @@ +fn tricky(x: i32) -> i32 + one = constant(i32, 1) + two = constant(i32, 2) + loop = region(start, if2_true) + idx = phi(loop, x, idx_dec) + val = phi(loop, one, later_val) + b = ne(one, val) + if1 = if(loop, b) + if1_false = projection(if1, 0) + if1_true = projection(if1, 1) + middle = region(if1_false, if1_true) + inter_val = sub(two, val) + later_val = phi(middle, inter_val, two) + idx_dec = sub(idx, one) + cond = gte(idx_dec, one) + if2 = if(middle, cond) + if2_false = projection(if2, 0) + if2_true = projection(if2, 1) + r = return(if2_false, later_val) diff --git a/hercules_test/test_inputs/fork_join.hir b/hercules_test/test_inputs/fork_join.hir new file mode 100644 index 00000000..99e95829 --- /dev/null +++ b/hercules_test/test_inputs/fork_join.hir @@ -0,0 +1,8 @@ +fn fork_join<1>() -> u64 + f_ctrl = fork(start, #0) + j_ctrl = join(f_ctrl) + zero = constant(u64, 0) + x = thread_id(f_ctrl) + data = reduce(j_ctrl, zero, sum) + sum = add(data, x) + r = return(j_ctrl, data) diff --git a/hercules_test/test_inputs/gvn_example.hir b/hercules_test/test_inputs/gvn_example.hir new file mode 100644 index 00000000..b5e3c8aa --- /dev/null +++ b/hercules_test/test_inputs/gvn_example.hir @@ -0,0 +1,8 @@ +fn tricky(x: i32, y: i32) -> i32 + zero = constant(i32, 0) + xx = add(x, zero) + a = add(xx, y) + b = add(x, y) + bb = add(zero, b) + c = add(a, bb) + r = return(start, c) diff --git a/hercules_test/test_inputs/matmul.hir b/hercules_test/test_inputs/matmul.hir new file mode 100644 index 00000000..2f0fb67a --- /dev/null +++ b/hercules_test/test_inputs/matmul.hir @@ -0,0 +1,58 @@ +fn matmul<3>(a: array(f32, #0, #1), b: array(f32, #1, #2)) -> array(f32, #0, #2) + c = constant(array(f32, #0, #2), zero) + i_ctrl = fork(start, #0) + i_idx = thread_id(i_ctrl) + j_ctrl = fork(i_ctrl, #2) + j_idx = thread_id(j_ctrl) + k_ctrl = fork(j_ctrl, #1) + k_idx = thread_id(k_ctrl) + k_join_ctrl = join(k_ctrl) + j_join_ctrl = join(k_join_ctrl) + i_join_ctrl = join(j_join_ctrl) + r = return(i_join_ctrl, update_i_c) + zero = constant(f32, 0) + a_val = read(a, position(i_idx, k_idx)) + b_val = read(b, position(k_idx, j_idx)) + mul = mul(a_val, b_val) + add = add(mul, dot) + dot = reduce(k_join_ctrl, zero, add) + updated_c = write(update_j_c, dot, position(i_idx, j_idx)) + update_j_c = reduce(j_join_ctrl, update_i_c, updated_c) + update_i_c = reduce(i_join_ctrl, c, update_j_c) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hercules_test/test_inputs/matmul_int.hir b/hercules_test/test_inputs/matmul_int.hir new file mode 100644 index 00000000..1e496bab --- /dev/null +++ b/hercules_test/test_inputs/matmul_int.hir @@ -0,0 +1,21 @@ +fn matmul<3>(a: array(i32, #0, #1), b: array(i32, #1, #2)) -> array(i32, #0, #2) + c = constant(array(i32, #0, #2), []) + i_ctrl = fork(start, #0) + i_idx = thread_id(i_ctrl) + j_ctrl = fork(i_ctrl, #2) + j_idx = thread_id(j_ctrl) + k_ctrl = fork(j_ctrl, #1) + k_idx = thread_id(k_ctrl) + k_join_ctrl = join(k_ctrl) + j_join_ctrl = join(k_join_ctrl) + i_join_ctrl = join(j_join_ctrl) + r = return(i_join_ctrl, update_i_c) + zero = constant(i32, 0) + a_val = read(a, position(i_idx, k_idx)) + b_val = read(b, position(k_idx, j_idx)) + mul = mul(a_val, b_val) + add = add(mul, dot) + dot = reduce(k_join_ctrl, zero, add) + updated_c = write(update_j_c, dot, position(i_idx, j_idx)) + update_j_c = reduce(j_join_ctrl, update_i_c, updated_c) + update_i_c = reduce(i_join_ctrl, c, update_j_c) diff --git a/hercules_test/test_inputs/simple1.hir b/hercules_test/test_inputs/simple1.hir new file mode 100644 index 00000000..902c1206 --- /dev/null +++ b/hercules_test/test_inputs/simple1.hir @@ -0,0 +1,3 @@ +fn simple1(a: i32, b: i32) -> i32 + c = add(a, b) + r = return(start, c) diff --git a/hercules_test/test_inputs/simple2.hir b/hercules_test/test_inputs/simple2.hir new file mode 100644 index 00000000..af5ac284 --- /dev/null +++ b/hercules_test/test_inputs/simple2.hir @@ -0,0 +1,13 @@ +fn simple2(x: i32) -> i32 + zero = constant(i32, 0) + one = constant(i32, 1) + loop = region(start, if_true) + idx = phi(loop, zero, idx_inc) + idx_inc = add(idx, one) + fac = phi(loop, one, fac_acc) + fac_acc = mul(fac, idx_inc) + in_bounds = lt(idx_inc, x) + if = if(loop, in_bounds) + if_false = projection(if, 0) + if_true = projection(if, 1) + r = return(if_false, fac_acc) \ No newline at end of file diff --git a/hercules_test/test_inputs/simple3.hir b/hercules_test/test_inputs/simple3.hir new file mode 100644 index 00000000..33a8e1b4 --- /dev/null +++ b/hercules_test/test_inputs/simple3.hir @@ -0,0 +1,11 @@ +fn simple3<1>(a: array(f32, #0), b: array(f32, #0)) -> f32 + zero = constant(f32, 0.0) + fork = fork(start, #0) + id = thread_id(fork) + join = join(fork) + r = return(join, dot_red) + aval = read(a, position(id)) + bval = read(b, position(id)) + mul = mul(aval, bval) + dot = add(mul, dot_red) + dot_red = reduce(join, zero, dot) \ No newline at end of file diff --git a/hercules_test/test_inputs/strset.hir b/hercules_test/test_inputs/strset.hir new file mode 100644 index 00000000..4c8b32ee --- /dev/null +++ b/hercules_test/test_inputs/strset.hir @@ -0,0 +1,17 @@ +fn strset<1>(str: array(u8, #0), byte: u8) -> array(u8, #0) + zero = constant(u64, 0) + one = constant(u64, 1) + bound = dynamic_constant(#0) + loop = region(start, if_true) + idx = phi(loop, zero, idx_inc) + str_inc = phi(loop, str, write) + idx_inc = add(idx, one) + in_bounds = lt(idx_inc, bound) + read = read(str_inc, position(idx)) + write = write(str_inc, byte, position(idx)) + continue = ne(read, byte) + if_cond = and(continue, in_bounds) + if = if(loop, if_cond) + if_false = projection(if, 0) + if_true = projection(if, 1) + r = return(if_false, str_inc) diff --git a/hercules_test/test_inputs/sum_int1.hir b/hercules_test/test_inputs/sum_int1.hir new file mode 100644 index 00000000..4a9ba015 --- /dev/null +++ b/hercules_test/test_inputs/sum_int1.hir @@ -0,0 +1,16 @@ +fn sum<1>(a: array(i32, #0)) -> i32 + zero_idx = constant(u64, 0) + one_idx = constant(u64, 1) + zero_inc = constant(i32, 0) + bound = dynamic_constant(#0) + loop = region(start, if_true) + idx = phi(loop, zero_idx, idx_inc) + idx_inc = add(idx, one_idx) + red = phi(loop, zero_inc, red_add) + read = read(a, position(idx)) + red_add = add(red, read) + in_bounds = lt(idx_inc, bound) + if = if(loop, in_bounds) + if_false = projection(if, 0) + if_true = projection(if, 1) + r = return(if_false, red_add) \ No newline at end of file diff --git a/hercules_test/test_inputs/sum_int2.hir b/hercules_test/test_inputs/sum_int2.hir new file mode 100644 index 00000000..b5e9a5c0 --- /dev/null +++ b/hercules_test/test_inputs/sum_int2.hir @@ -0,0 +1,25 @@ +fn alt_sum<1>(a: array(i32, #0)) -> i32 + zero_idx = constant(u64, 0) + one_idx = constant(u64, 1) + two_idx = constant(u64, 2) + zero_inc = constant(i32, 0) + bound = dynamic_constant(#0) + loop = region(start, if_true) + idx = phi(loop, zero_idx, idx_inc) + idx_inc = add(idx, one_idx) + red = phi(loop, zero_inc, red_add) + rem = rem(idx, two_idx) + odd = eq(rem, one_idx) + negate_if = if(loop, odd) + negate_if_false = projection(negate_if, 0) + negate_if_true = projection(negate_if, 1) + negate_bottom = region(negate_if_false, negate_if_true) + read = read(a, position(idx)) + read_neg = neg(read) + read_phi = phi(negate_bottom, read, read_neg) + red_add = add(red, read_phi) + in_bounds = lt(idx_inc, bound) + if = if(negate_bottom, in_bounds) + if_false = projection(if, 0) + if_true = projection(if, 1) + r = return(if_false, red_add) diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..271800cb --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file -- GitLab