From 327e3a4a1aeddb6ea07a753eed8046d7daed3673 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Sat, 9 Feb 2019 15:53:40 -0800 Subject: [PATCH] Implement many wasm instructions --- Cargo.lock | 169 ++++++- Cargo.toml | 2 +- lib/clif-backend/src/func_env.rs | 134 ++--- lib/clif-backend/src/module_env.rs | 6 +- lib/clif-backend/src/signal/mod.rs | 2 +- lib/llvm-backend/.llvmenv | 1 + lib/llvm-backend/Cargo.toml | 17 + lib/llvm-backend/src/code.rs | 765 +++++++++++++++++++++++++++++ lib/llvm-backend/src/example.rs | 61 +++ lib/llvm-backend/src/intrinsics.rs | 99 ++++ lib/llvm-backend/src/lib.rs | 52 ++ lib/llvm-backend/src/read_info.rs | 333 +++++++++++++ lib/llvm-backend/src/state.rs | 105 ++++ lib/runtime-core/src/backing.rs | 10 +- lib/runtime-core/src/instance.rs | 18 +- lib/runtime-core/src/types.rs | 19 +- lib/runtime-core/src/vm.rs | 2 +- 17 files changed, 1700 insertions(+), 95 deletions(-) create mode 100644 lib/llvm-backend/.llvmenv create mode 100644 lib/llvm-backend/Cargo.toml create mode 100644 lib/llvm-backend/src/code.rs create mode 100644 lib/llvm-backend/src/example.rs create mode 100644 lib/llvm-backend/src/intrinsics.rs create mode 100644 lib/llvm-backend/src/lib.rs create mode 100644 lib/llvm-backend/src/read_info.rs create mode 100644 lib/llvm-backend/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index d061df27b..209d06782 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,11 @@ +[[package]] +name = "aho-corasick" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -28,7 +36,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "block-buffer" -version = "0.7.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -178,6 +186,20 @@ dependencies = [ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "either" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "enum-methods" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "errno" version = "0.2.4" @@ -281,6 +303,27 @@ name = "indexmap" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/TheDan64/inkwell?branch=llvm7-0#83b9188bad1e098f97e6aad8f6d45becb6d1ccdd" +dependencies = [ + "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell_internal_macros 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm7-0)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "llvm-sys 70.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inkwell_internal_macros" +version = "0.1.0" +source = "git+https://github.com/TheDan64/inkwell?branch=llvm7-0#83b9188bad1e098f97e6aad8f6d45becb6d1ccdd" +dependencies = [ + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.4.3" @@ -310,6 +353,29 @@ name = "libc" version = "0.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "llvm-backend" +version = "0.1.0" +dependencies = [ + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm7-0)", + "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.1.2", + "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "llvm-sys" +version = "70.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lock_api" version = "0.1.5" @@ -327,6 +393,15 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memchr" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "memmap" version = "0.7.0" @@ -412,6 +487,11 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "0.6.11" @@ -547,6 +627,26 @@ dependencies = [ "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc_version" version = "0.2.3" @@ -625,7 +725,7 @@ name = "sha2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -669,6 +769,16 @@ dependencies = [ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.26" @@ -679,6 +789,14 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synstructure" version = "0.10.1" @@ -718,6 +836,14 @@ dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "time" version = "0.1.42" @@ -733,6 +859,11 @@ name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-segmentation" version = "1.2.1" @@ -743,6 +874,11 @@ name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -756,6 +892,11 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "utf8-ranges" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vec_map" version = "0.8.1" @@ -886,6 +1027,11 @@ name = "wasmparser" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wasmparser" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.2.8" @@ -916,11 +1062,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "509de513cca6d92b6aacf9c61acfe7eaa160837323a81068d690cc1f8e5740da" +"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d" "checksum block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d75255892aeb580d3c566f213a2b6fdc1c66667839f45719ee1d30ebf2aea591" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" @@ -938,6 +1085,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "474bee81d620a473bf43411a3d6f10ffbf7965141dc5e5b76d8d2151dde3285d" "checksum cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49723365dab9a48b354bdc24cb6d9d5719bc1d3b858ffd2ea179d0d7d885804a" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" +"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" +"checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" "checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" "checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" @@ -952,13 +1101,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +"checksum inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm7-0)" = "" +"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm7-0)" = "" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" "checksum libc 0.2.48 (git+https://github.com/rust-lang/libc)" = "" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" +"checksum llvm-sys 70.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60a9ee82fe0fa72ae6ef6d018b407296085863836451c7a97384f84ed7e26b9f" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1dd4eaac298c32ce07eb6ed9242eda7d82955b9170b7d6db59b2e02cc63fcb8" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f" "checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b" @@ -968,6 +1121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" @@ -983,6 +1137,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" +"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" @@ -999,23 +1155,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3" "checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "74e463a508e390cc7447e70f640fbf44ad52e1bd095314ace1fdf99516d32add" "checksum wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a6265b25719e82598d104b3717375e37661d41753e2c84cde3f51050c7ed7e3c" "checksum wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e666ecb4a406483a59a49f9d0c17f327e70da53a128eccddae2eadb95865c" "checksum wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5e01c420bc7d36e778bd242e1167b079562ba8b34087122cc9057187026d060" +"checksum wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40f426b1929bd26517fb10702e2a8e520d1845c49567aa4d244f426f10b206c1" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index b570c9b09..77bf8a4b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-emscripten = { path = "lib/emscripten" } [workspace] -members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests"] +members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/llvm-backend"] [build-dependencies] wabt = "0.7.2" diff --git a/lib/clif-backend/src/func_env.rs b/lib/clif-backend/src/func_env.rs index 789195264..f24cfc433 100644 --- a/lib/clif-backend/src/func_env.rs +++ b/lib/clif-backend/src/func_env.rs @@ -75,7 +75,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let ptr_type = self.pointer_type(); - let local_global_addr = match global_index.local_or_import(self.env.module) { + let local_global_addr = match global_index.local_or_import(&self.env.module.info) { LocalOrImport::Local(local_global_index) => { let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load { base: vmctx, @@ -145,48 +145,49 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let ptr_type = self.pointer_type(); - let (local_memory_ptr_ptr, description) = match mem_index.local_or_import(self.env.module) { - LocalOrImport::Local(local_mem_index) => { - let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: (vm::Ctx::offset_memories() as i32).into(), - global_type: ptr_type, - readonly: true, - }); - - let local_memory_ptr_offset = - local_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); - - ( - func.create_global_value(ir::GlobalValueData::IAddImm { - base: memories_base_addr, - offset: (local_memory_ptr_offset as i64).into(), + let (local_memory_ptr_ptr, description) = + match mem_index.local_or_import(&self.env.module.info) { + LocalOrImport::Local(local_mem_index) => { + let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: (vm::Ctx::offset_memories() as i32).into(), global_type: ptr_type, - }), - self.env.module.info.memories[local_mem_index], - ) - } - LocalOrImport::Import(import_mem_index) => { - let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: (vm::Ctx::offset_imported_memories() as i32).into(), - global_type: ptr_type, - readonly: true, - }); + readonly: true, + }); - let local_memory_ptr_offset = - import_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); + let local_memory_ptr_offset = + local_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); - ( - func.create_global_value(ir::GlobalValueData::IAddImm { - base: memories_base_addr, - offset: (local_memory_ptr_offset as i64).into(), + ( + func.create_global_value(ir::GlobalValueData::IAddImm { + base: memories_base_addr, + offset: (local_memory_ptr_offset as i64).into(), + global_type: ptr_type, + }), + self.env.module.info.memories[local_mem_index], + ) + } + LocalOrImport::Import(import_mem_index) => { + let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: (vm::Ctx::offset_imported_memories() as i32).into(), global_type: ptr_type, - }), - self.env.module.info.imported_memories[import_mem_index].1, - ) - } - }; + readonly: true, + }); + + let local_memory_ptr_offset = + import_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); + + ( + func.create_global_value(ir::GlobalValueData::IAddImm { + base: memories_base_addr, + offset: (local_memory_ptr_offset as i64).into(), + global_type: ptr_type, + }), + self.env.module.info.imported_memories[import_mem_index].1, + ) + } + }; let (local_memory_ptr, local_memory_base) = { let local_memory_ptr = func.create_global_value(ir::GlobalValueData::Load { @@ -253,7 +254,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let ptr_type = self.pointer_type(); - let (table_struct_ptr_ptr, description) = match table_index.local_or_import(self.env.module) + let (table_struct_ptr_ptr, description) = match table_index + .local_or_import(&self.env.module.info) { LocalOrImport::Local(local_table_index) => { let tables_base = func.create_global_value(ir::GlobalValueData::Load { @@ -476,7 +478,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { ) -> cranelift_wasm::WasmResult { let callee_index: FuncIndex = Converter(clif_callee_index).into(); - match callee_index.local_or_import(self.env.module) { + match callee_index.local_or_import(&self.env.module.info) { LocalOrImport::Local(_) => { // this is an internal function let vmctx = pos @@ -568,18 +570,19 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let mem_index: MemoryIndex = Converter(clif_mem_index).into(); - let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) { - LocalOrImport::Local(local_mem_index) => ( - call_names::LOCAL_NAMESPACE, - local_mem_index.index(), - self.env.module.info.memories[local_mem_index], - ), - LocalOrImport::Import(import_mem_index) => ( - call_names::IMPORT_NAMESPACE, - import_mem_index.index(), - self.env.module.info.imported_memories[import_mem_index].1, - ), - }; + let (namespace, mem_index, description) = + match mem_index.local_or_import(&self.env.module.info) { + LocalOrImport::Local(local_mem_index) => ( + call_names::LOCAL_NAMESPACE, + local_mem_index.index(), + self.env.module.info.memories[local_mem_index], + ), + LocalOrImport::Import(import_mem_index) => ( + call_names::IMPORT_NAMESPACE, + import_mem_index.index(), + self.env.module.info.imported_memories[import_mem_index].1, + ), + }; let name_index = match description.memory_type() { MemoryType::Dynamic => call_names::DYNAMIC_MEM_GROW, @@ -631,18 +634,19 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let mem_index: MemoryIndex = Converter(clif_mem_index).into(); - let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) { - LocalOrImport::Local(local_mem_index) => ( - call_names::LOCAL_NAMESPACE, - local_mem_index.index(), - self.env.module.info.memories[local_mem_index], - ), - LocalOrImport::Import(import_mem_index) => ( - call_names::IMPORT_NAMESPACE, - import_mem_index.index(), - self.env.module.info.imported_memories[import_mem_index].1, - ), - }; + let (namespace, mem_index, description) = + match mem_index.local_or_import(&self.env.module.info) { + LocalOrImport::Local(local_mem_index) => ( + call_names::LOCAL_NAMESPACE, + local_mem_index.index(), + self.env.module.info.memories[local_mem_index], + ), + LocalOrImport::Import(import_mem_index) => ( + call_names::IMPORT_NAMESPACE, + import_mem_index.index(), + self.env.module.info.imported_memories[import_mem_index].1, + ), + }; let name_index = match description.memory_type() { MemoryType::Dynamic => call_names::DYNAMIC_MEM_SIZE, diff --git a/lib/clif-backend/src/module_env.rs b/lib/clif-backend/src/module_env.rs index de625cc23..9ff85dcf3 100644 --- a/lib/clif-backend/src/module_env.rs +++ b/lib/clif-backend/src/module_env.rs @@ -136,7 +136,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> // assert!(!desc.mutable); let global_index: GlobalIndex = Converter(global_index).into(); let imported_global_index = global_index - .local_or_import(self.module) + .local_or_import(&self.module.info) .import() .expect("invalid global initializer when declaring an imported global"); Initializer::GetGlobal(imported_global_index) @@ -246,7 +246,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let base = match base { Some(global_index) => { let global_index: GlobalIndex = Converter(global_index).into(); - Initializer::GetGlobal(match global_index.local_or_import(self.module) { + Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) { LocalOrImport::Import(imported_global_index) => imported_global_index, LocalOrImport::Local(_) => { panic!("invalid global initializer when declaring an imported global") @@ -316,7 +316,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let base = match base { Some(global_index) => { let global_index: GlobalIndex = Converter(global_index).into(); - Initializer::GetGlobal(match global_index.local_or_import(self.module) { + Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) { LocalOrImport::Import(imported_global_index) => imported_global_index, LocalOrImport::Local(_) => { panic!("invalid global initializer when declaring an imported global") diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 0767f9527..1ff73e055 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -148,7 +148,7 @@ fn get_func_from_index<'a>( .get(func_index) .expect("broken invariant, incorrect func index"); - let (func_ptr, ctx) = match func_index.local_or_import(module) { + let (func_ptr, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver diff --git a/lib/llvm-backend/.llvmenv b/lib/llvm-backend/.llvmenv new file mode 100644 index 000000000..6d1e3080f --- /dev/null +++ b/lib/llvm-backend/.llvmenv @@ -0,0 +1 @@ +/usr/local/opt/llvm/bin \ No newline at end of file diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml new file mode 100644 index 000000000..000265b7d --- /dev/null +++ b/lib/llvm-backend/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "llvm-backend" +version = "0.1.0" +authors = ["Lachlan Sneff "] +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } +wasmparser = "0.28.0" +inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" } +hashbrown = "0.1.8" + +[dev-dependencies] +wabt = "0.7.4" + +[features] +debug = ["wasmer-runtime-core/debug"] \ No newline at end of file diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs new file mode 100644 index 000000000..50a31065a --- /dev/null +++ b/lib/llvm-backend/src/code.rs @@ -0,0 +1,765 @@ +use hashbrown::HashMap; +use inkwell::{ + basic_block::BasicBlock, + builder::Builder, + context::Context, + module::Module, + types::{BasicType, BasicTypeEnum, FunctionType}, + values::{AggregateValue, BasicValue, BasicValueEnum, FunctionValue}, + IntPredicate, +}; +use wasmer_runtime_core::{ + module::ModuleInfo, + structures::{Map, SliceMap, TypedIndex}, + types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type}, +}; +use wasmparser::{BinaryReaderError, CodeSectionReader, LocalsReader, Operator, OperatorsReader}; + +use crate::intrinsics::Intrinsics; +use crate::read_info::type_to_type; +use crate::state::State; + +fn func_sig_to_llvm(context: &Context, sig: &FuncSig) -> FunctionType { + let param_types: Vec<_> = sig + .params() + .iter() + .map(|&ty| type_to_llvm(context, ty)) + .collect(); + + match sig.returns() { + [] => context.void_type().fn_type(¶m_types, false), + [single_value] => type_to_llvm(context, *single_value).fn_type(¶m_types, false), + returns @ _ => { + let basic_types: Vec<_> = returns + .iter() + .map(|&ty| type_to_llvm(context, ty)) + .collect(); + + context + .struct_type(&basic_types, false) + .fn_type(¶m_types, false) + } + } +} + +fn type_to_llvm(context: &Context, ty: Type) -> BasicTypeEnum { + match ty { + Type::I32 => context.i32_type().as_basic_type_enum(), + Type::I64 => context.i64_type().as_basic_type_enum(), + Type::F32 => context.f32_type().as_basic_type_enum(), + Type::F64 => context.f64_type().as_basic_type_enum(), + } +} + +pub fn parse_function_bodies( + info: &ModuleInfo, + code_reader: CodeSectionReader, +) -> Result<(), BinaryReaderError> { + let context = Context::create(); + let module = context.create_module("module"); + let builder = context.create_builder(); + + let intrinsics = Intrinsics::declare(&module, &context); + + let signatures: Map = info + .signatures + .iter() + .map(|(_, sig)| func_sig_to_llvm(&context, sig)) + .collect(); + let functions: Map = info + .func_assoc + .iter() + .skip(info.imported_functions.len()) + .map(|(func_index, &sig_index)| { + module.add_function( + &format!("fn:{}", func_index.index()), + signatures[sig_index], + None, + ) + }) + .collect(); + + for (local_func_index, body) in code_reader.into_iter().enumerate() { + let body = body?; + + let locals_reader = body.get_locals_reader()?; + let op_reader = body.get_operators_reader()?; + + parse_function( + &context, + &module, + &builder, + &intrinsics, + info, + &signatures, + &functions, + LocalFuncIndex::new(local_func_index), + locals_reader, + op_reader, + )?; + } + + Ok(()) +} + +fn parse_function( + context: &Context, + module: &Module, + builder: &Builder, + intrinsics: &Intrinsics, + info: &ModuleInfo, + signatures: &SliceMap, + functions: &SliceMap, + func_index: LocalFuncIndex, + locals_reader: LocalsReader, + op_reader: OperatorsReader, +) -> Result<(), BinaryReaderError> { + let llvm_sig = &signatures[info.func_assoc[func_index.convert_up(info)]]; + + let function = functions[func_index]; + let entry_block = context.append_basic_block(&function, "entry"); + builder.position_at_end(&entry_block); + + let mut state = State::new(); + + let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); + locals.extend(function.get_param_iter().enumerate().map(|(index, param)| { + let ty = param.get_type(); + + let alloca = builder.build_alloca(ty, &state.var_name()); + builder.build_store(alloca, param); + alloca + })); + + for (index, local) in locals_reader.into_iter().enumerate().skip(locals.len()) { + let (_, ty) = local?; + + let wasmer_ty = type_to_type(ty)?; + + let ty = type_to_llvm(context, wasmer_ty); + + let alloca = builder.build_alloca(ty, &state.var_name()); + + let default_value = match wasmer_ty { + Type::I32 => context.i32_type().const_int(0, false).as_basic_value_enum(), + Type::I64 => context.i64_type().const_int(0, false).as_basic_value_enum(), + Type::F32 => context.f32_type().const_float(0.0).as_basic_value_enum(), + Type::F64 => context.f64_type().const_float(0.0).as_basic_value_enum(), + }; + + builder.build_store(alloca, default_value); + + locals.push(alloca); + } + + for op in op_reader { + match op? { + /*************************** + * Basic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#basic-instructions + ***************************/ + Operator::Nop => { + // Do nothing. + } + Operator::Drop => { + state.pop1()?; + } + + // Generate const values. + Operator::I32Const { value } => { + let i = context.i32_type().const_int(value as u64, false); + state.push1(i); + } + Operator::I64Const { value } => { + let i = context.i64_type().const_int(value as u64, false); + state.push1(i); + } + Operator::F32Const { value } => { + let f = context + .f32_type() + .const_float(f64::from_bits(value.bits() as u64)); + state.push1(f); + } + Operator::F64Const { value } => { + let f = context.f64_type().const_float(f64::from_bits(value.bits())); + state.push1(f); + } + + // Operate on locals. + Operator::GetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = builder.build_load(pointer_value, &state.var_name()); + state.push1(v); + } + Operator::SetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.pop1()?; + builder.build_store(pointer_value, v); + } + Operator::TeeLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.peek1()?; + builder.build_store(pointer_value, v); + } + + Operator::GetGlobal { global_index } => unimplemented!(), + Operator::SetGlobal { global_index } => unimplemented!(), + + Operator::Select => { + let (v1, v2, cond) = state.pop3()?; + let cond = cond.into_int_value(); + let res = builder.build_select(cond, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::Call { function_index } => { + let func_index = FuncIndex::new(function_index as usize); + let sigindex = info.func_assoc[func_index]; + let llvm_sig = signatures[sigindex]; + + match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => { + let func_sig = &info.signatures[sigindex]; + let func_value = functions[local_func_index]; + let call_site = builder.build_call( + func_value, + &state.peekn(func_sig.params().len())?.to_vec(), + &state.var_name(), + ); + if let Some(basic_value) = call_site.try_as_basic_value().left() { + match func_sig.returns().len() { + 1 => state.push1(basic_value), + count @ _ => { + // This is a multi-value return. + let struct_value = basic_value.into_struct_value(); + for i in 0..(count as u32) { + let value = builder.build_extract_value( + struct_value, + i, + &state.var_name(), + ); + state.push1(value); + } + } + } + } + } + LocalOrImport::Import(import_func_index) => { + // unimplemented!() + } + } + } + Operator::CallIndirect { index, table_index } => { + unimplemented!("{}, {}", index, table_index); + } + + /*************************** + * Integer Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-arithmetic-instructions + ***************************/ + Operator::I32Add | Operator::I64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Sub | Operator::I64Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Mul | Operator::I64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivS | Operator::I64DivS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_signed_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivU | Operator::I64DivU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_unsigned_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemS | Operator::I64RemS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_signed_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemU | Operator::I64RemU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_unsigned_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32And | Operator::I64And => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_and(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Or | Operator::I64Or => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_or(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Xor | Operator::I64Xor => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_xor(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Shl | Operator::I64Shl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_left_shift(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32ShrS | Operator::I64ShrS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, true, &state.var_name()); + state.push1(res); + } + Operator::I32ShrU | Operator::I64ShrU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, false, &state.var_name()); + state.push1(res); + } + Operator::I32Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = context.i32_type().const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = context.i64_type().const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = context.i32_type().const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = context.i64_type().const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Clz => { + let input = state.pop1()?; + let ensure_defined_zero = context + .bool_type() + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Clz => { + let input = state.pop1()?; + let ensure_defined_zero = context + .bool_type() + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = context + .bool_type() + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = context + .bool_type() + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Eqz => { + let input = state.pop1()?.into_int_value(); + let zero = context.i32_type().const_int(0, false); + let res = + builder.build_int_compare(IntPredicate::EQ, input, zero, &state.var_name()); + state.push1(res); + } + Operator::I64Eqz => { + let input = state.pop1()?.into_int_value(); + let zero = context.i64_type().const_int(0, false); + let res = + builder.build_int_compare(IntPredicate::EQ, input, zero, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-arithmetic-instructions + ***************************/ + Operator::F32Add | Operator::F64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sub | Operator::F32Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Mul | Operator::F64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Div | Operator::F64Div => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Neg | Operator::F64Neg => { + let input = state.pop1()?.into_float_value(); + let res = builder.build_float_neg(input, &state.var_name()); + state.push1(res); + } + Operator::F32Copysign => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.copysign_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Copysign => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.copysign_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + + /*************************** + * Integer Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-comparison-instructions + ***************************/ + Operator::I32Eq | Operator::I64Eq => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Ne | Operator::I64Ne => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32LtS | Operator::I64LtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32LtU | Operator::I64LtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32LeS | Operator::I64LeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32LeU | Operator::I64LeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32GtS | Operator::I64GtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32GtU | Operator::I64GtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32GeS | Operator::I64GeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32GeU | Operator::I64GeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-comparison-instructions + ***************************/ + Operator::Unreachable => { + // Emit an unreachable instruction. + // If llvm cannot prove that this is never touched, + // it will emit a `ud2` instruction on x86_64 arches. + builder.build_unreachable(); + } + op @ _ => { + println!("{}", module.print_to_string().to_string()); + unimplemented!("{:?}", op); + } + } + } + + Ok(()) +} diff --git a/lib/llvm-backend/src/example.rs b/lib/llvm-backend/src/example.rs new file mode 100644 index 000000000..5ce3c9a5f --- /dev/null +++ b/lib/llvm-backend/src/example.rs @@ -0,0 +1,61 @@ +use inkwell::OptimizationLevel; +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::execution_engine::{ExecutionEngine, JitFunction}; +use inkwell::module::Module; +use inkwell::targets::{InitializationConfig, Target}; +use std::error::Error; + +/// Convenience type alias for the `sum` function. +/// +/// Calling this is innately `unsafe` because there's no guarantee it doesn't +/// do `unsafe` operations internally. +type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; + +#[test] +fn test_sum() -> Result<(), Box> { + let context = Context::create(); + let module = context.create_module("sum"); + let builder = context.create_builder(); + let execution_engine = module.create_jit_execution_engine(OptimizationLevel::Aggressive)?; + + let sum = jit_compile_sum(&context, &module, &builder, &execution_engine) + .ok_or("Unable to JIT compile `sum`")?; + + let x = 1u64; + let y = 2u64; + let z = 3u64; + + unsafe { + println!("{} + {} + {} = {}", x, y, z, sum.call(x, y, z)); + assert_eq!(sum.call(x, y, z), x + y + z); + } + + Ok(()) +} + +fn jit_compile_sum( + context: &Context, + module: &Module, + builder: &Builder, + execution_engine: &ExecutionEngine, +) -> Option> { + let i64_type = context.i64_type(); + let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); + + let function = module.add_function("sum", fn_type, None); + let basic_block = context.append_basic_block(&function, "entry"); + + builder.position_at_end(&basic_block); + + let x = function.get_nth_param(0)?.into_int_value(); + let y = function.get_nth_param(1)?.into_int_value(); + let z = function.get_nth_param(2)?.into_int_value(); + + let sum = builder.build_int_add(x, y, "sum"); + let sum = builder.build_int_add(sum, z, "sum"); + + builder.build_return(Some(&sum)); + + unsafe { execution_engine.get_function("sum").ok() } +} diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs new file mode 100644 index 000000000..824317e8e --- /dev/null +++ b/lib/llvm-backend/src/intrinsics.rs @@ -0,0 +1,99 @@ +use inkwell::{context::Context, module::Module, types::BasicType, values::FunctionValue}; + +pub struct Intrinsics { + pub ctlz_i32: FunctionValue, + pub ctlz_i64: FunctionValue, + + pub cttz_i32: FunctionValue, + pub cttz_i64: FunctionValue, + + pub ctpop_i32: FunctionValue, + pub ctpop_i64: FunctionValue, + + pub sqrt_f32: FunctionValue, + pub sqrt_f64: FunctionValue, + + pub minimum_f32: FunctionValue, + pub minimum_f64: FunctionValue, + + pub maximum_f32: FunctionValue, + pub maximum_f64: FunctionValue, + + pub ceil_f32: FunctionValue, + pub ceil_f64: FunctionValue, + + pub floor_f32: FunctionValue, + pub floor_f64: FunctionValue, + + pub trunc_f32: FunctionValue, + pub trunc_f64: FunctionValue, + + pub nearbyint_f32: FunctionValue, + pub nearbyint_f64: FunctionValue, + + pub fabs_f32: FunctionValue, + pub fabs_f64: FunctionValue, + + pub copysign_f32: FunctionValue, + pub copysign_f64: FunctionValue, +} + +impl Intrinsics { + pub fn declare(module: &Module, context: &Context) -> Self { + let i1_ty = context.bool_type().as_basic_type_enum(); + let i32_ty = context.i32_type().as_basic_type_enum(); + let i64_ty = context.i64_type().as_basic_type_enum(); + let f32_ty = context.f32_type().as_basic_type_enum(); + let f64_ty = context.f64_type().as_basic_type_enum(); + + let ret_i32_take_i32_i1 = i32_ty.fn_type(&[i32_ty, i1_ty], false); + let ret_i64_take_i64_i1 = i64_ty.fn_type(&[i64_ty, i1_ty], false); + + let ret_i32_take_i32 = i32_ty.fn_type(&[i32_ty], false); + let ret_i64_take_i64 = i64_ty.fn_type(&[i64_ty], false); + + let ret_f32_take_f32 = f32_ty.fn_type(&[f32_ty], false); + let ret_f64_take_f64 = f64_ty.fn_type(&[f64_ty], false); + + let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty, f32_ty], false); + let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty, f64_ty], false); + + Self { + ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None), + ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None), + + cttz_i32: module.add_function("llvm.cttz.i32", ret_i32_take_i32_i1, None), + cttz_i64: module.add_function("llvm.cttz.i64", ret_i64_take_i64_i1, None), + + ctpop_i32: module.add_function("llvm.ctpop.i32", ret_i32_take_i32, None), + ctpop_i64: module.add_function("llvm.ctpop.i64", ret_i64_take_i64, None), + + sqrt_f32: module.add_function("llvm.sqrt.f32", ret_f32_take_f32, None), + sqrt_f64: module.add_function("llvm.sqrt.f64", ret_f64_take_f64, None), + + minimum_f32: module.add_function("llvm.minimum.f32", ret_f32_take_f32_f32, None), + minimum_f64: module.add_function("llvm.minimum.f64", ret_f64_take_f64_f64, None), + + maximum_f32: module.add_function("llvm.maximum.f32", ret_f32_take_f32_f32, None), + maximum_f64: module.add_function("llvm.maximum.f64", ret_f64_take_f64_f64, None), + + ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None), + ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None), + + floor_f32: module.add_function("llvm.floor.f32", ret_f32_take_f32, None), + floor_f64: module.add_function("llvm.floor.f64", ret_f64_take_f64, None), + + trunc_f32: module.add_function("llvm.trunc.f32", ret_f32_take_f32, None), + trunc_f64: module.add_function("llvm.trunc.f64", ret_f64_take_f64, None), + + nearbyint_f32: module.add_function("llvm.nearbyint.f32", ret_f32_take_f32, None), + nearbyint_f64: module.add_function("llvm.nearbyint.f64", ret_f64_take_f64, None), + + fabs_f32: module.add_function("llvm.fabs.f32", ret_f32_take_f32, None), + fabs_f64: module.add_function("llvm.fabs.f64", ret_f64_take_f64, None), + + copysign_f32: module.add_function("llvm.copysign.f32", ret_f32_take_f32_f32, None), + copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None), + } + } +} diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs new file mode 100644 index 000000000..9b1b3eab6 --- /dev/null +++ b/lib/llvm-backend/src/lib.rs @@ -0,0 +1,52 @@ +use wasmer_runtime_core::{ + backend::{Compiler, Token}, + error::CompileError, + module::{ModuleInfo, ModuleInner}, +}; + +mod code; +mod intrinsics; +mod read_info; +mod state; + +pub struct LLVMCompiler { + _private: (), +} + +impl LLVMCompiler { + pub fn new() -> Self { + Self { _private: () } + } +} + +impl Compiler for LLVMCompiler { + fn compile(&self, wasm: &[u8], _: Token) -> Result { + let (_info, _code_reader) = read_info::read_module(wasm).unwrap(); + + unimplemented!() + } +} + +#[test] +fn test_read_module() { + use wabt::wat2wasm; + let WAT: &'static str = r#" + (module + (type $t0 (func (param i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (import "env" "table" (table 10 anyfunc)) + (import "env" "global" (global i32)) + (import "env" "print_i32" (func $print_i32 (type $t0))) + (func $identity (type $t0) (param $p0 i32) (result i32) + get_local $p0) + (func $print_num (export "print_num") (type $t0) (param $p0 i32) (result i32) + get_global 0 + call $identity + call $print_i32)) + "#; + let wasm = wat2wasm(WAT).unwrap(); + + let (info, code_reader) = read_info::read_module(&wasm).unwrap(); + + code::parse_function_bodies(&info, code_reader).unwrap(); +} diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs new file mode 100644 index 000000000..c1ac2cf6b --- /dev/null +++ b/lib/llvm-backend/src/read_info.rs @@ -0,0 +1,333 @@ +use wasmer_runtime_core::{ + backend::Backend, + module::{ + DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, + TableInitializer, + }, + structures::{Map, TypedIndex}, + types::{ + ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, + ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, + TableIndex, Type, Value, + }, + units::Pages, +}; +use wasmparser::{ + BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export, + ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, + SectionCode, Type as WpType, +}; + +pub fn read_module(wasm: &[u8]) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { + let mut info = ModuleInfo { + memories: Map::new(), + globals: Map::new(), + tables: Map::new(), + + imported_functions: Map::new(), + imported_memories: Map::new(), + imported_tables: Map::new(), + imported_globals: Map::new(), + + exports: Default::default(), + + data_initializers: Vec::new(), + elem_initializers: Vec::new(), + + start_func: None, + + func_assoc: Map::new(), + signatures: Map::new(), + backend: Backend::LLVM, + + namespace_table: StringTable::new(), + name_table: StringTable::new(), + }; + + let mut reader = ModuleReader::new(wasm)?; + let mut code_reader = None; + + loop { + if reader.eof() { + return Ok((info, code_reader.unwrap())); + } + + let section = reader.read()?; + + match section.code { + SectionCode::Type => { + let type_reader = section.get_type_section_reader()?; + + for ty in type_reader { + let ty = ty?; + info.signatures.push(func_type_to_func_sig(ty)?); + } + } + SectionCode::Import => { + let import_reader = section.get_import_section_reader()?; + let mut namespace_builder = StringTableBuilder::new(); + let mut name_builder = StringTableBuilder::new(); + + for import in import_reader { + let Import { module, field, ty } = import?; + + let namespace_index = namespace_builder.register(module); + let name_index = name_builder.register(field); + let import_name = ImportName { + namespace_index, + name_index, + }; + + match ty { + ImportSectionEntryType::Function(sigindex) => { + let sigindex = SigIndex::new(sigindex as usize); + info.imported_functions.push(import_name); + info.func_assoc.push(sigindex); + } + ImportSectionEntryType::Table(table_ty) => { + assert_eq!(table_ty.element_type, WpType::AnyFunc); + let table_desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: table_ty.limits.initial, + maximum: table_ty.limits.maximum, + }; + + info.imported_tables.push((import_name, table_desc)); + } + ImportSectionEntryType::Memory(memory_ty) => { + let mem_desc = MemoryDescriptor { + minimum: Pages(memory_ty.limits.initial), + maximum: memory_ty.limits.maximum.map(|max| Pages(max)), + shared: memory_ty.shared, + }; + info.imported_memories.push((import_name, mem_desc)); + } + ImportSectionEntryType::Global(global_ty) => { + let global_desc = GlobalDescriptor { + mutable: global_ty.mutable, + ty: type_to_type(global_ty.content_type)?, + }; + info.imported_globals.push((import_name, global_desc)); + } + } + } + } + SectionCode::Function => { + let func_decl_reader = section.get_function_section_reader()?; + + for sigindex in func_decl_reader { + let sigindex = sigindex?; + + let sigindex = SigIndex::new(sigindex as usize); + info.func_assoc.push(sigindex); + } + } + SectionCode::Table => { + let table_decl_reader = section.get_table_section_reader()?; + + for table_ty in table_decl_reader { + let table_ty = table_ty?; + + let table_desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: table_ty.limits.initial, + maximum: table_ty.limits.maximum, + }; + + info.tables.push(table_desc); + } + } + SectionCode::Memory => { + let mem_decl_reader = section.get_memory_section_reader()?; + + for memory_ty in mem_decl_reader { + let memory_ty = memory_ty?; + + let mem_desc = MemoryDescriptor { + minimum: Pages(memory_ty.limits.initial), + maximum: memory_ty.limits.maximum.map(|max| Pages(max)), + shared: memory_ty.shared, + }; + + info.memories.push(mem_desc); + } + } + SectionCode::Global => { + let global_decl_reader = section.get_global_section_reader()?; + + for global in global_decl_reader { + let global = global?; + + let desc = GlobalDescriptor { + mutable: global.ty.mutable, + ty: type_to_type(global.ty.content_type)?, + }; + + let global_init = GlobalInit { + desc, + init: eval_init_expr(&global.init_expr)?, + }; + + info.globals.push(global_init); + } + } + SectionCode::Export => { + let export_reader = section.get_export_section_reader()?; + + for export in export_reader { + let Export { field, kind, index } = export?; + + let export_index = match kind { + ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), + ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), + ExternalKind::Memory => { + ExportIndex::Memory(MemoryIndex::new(index as usize)) + } + ExternalKind::Global => { + ExportIndex::Global(GlobalIndex::new(index as usize)) + } + }; + + info.exports.insert(field.to_string(), export_index); + } + } + SectionCode::Start => { + let start_index = section.get_start_section_content()?; + + info.start_func = Some(FuncIndex::new(start_index as usize)); + } + SectionCode::Element => { + let element_reader = section.get_element_section_reader()?; + + for element in element_reader { + let Element { kind, items } = element?; + + match kind { + ElementKind::Active { + table_index, + init_expr, + } => { + let table_index = TableIndex::new(table_index as usize); + let base = eval_init_expr(&init_expr)?; + let items_reader = items.get_items_reader()?; + + let elements: Vec<_> = items_reader + .into_iter() + .map(|res| res.map(|index| FuncIndex::new(index as usize))) + .collect::>()?; + + let table_init = TableInitializer { + table_index, + base, + elements, + }; + + info.elem_initializers.push(table_init); + } + ElementKind::Passive(_ty) => { + return Err(BinaryReaderError { + message: "passive tables are not yet supported", + offset: -1isize as usize, + }); + } + } + } + } + SectionCode::Code => { + code_reader = Some(section.get_code_section_reader()?); + } + SectionCode::Data => { + let data_reader = section.get_data_section_reader()?; + + for data in data_reader { + let Data { kind, data } = data?; + + match kind { + DataKind::Active { + memory_index, + init_expr, + } => { + let memory_index = MemoryIndex::new(memory_index as usize); + let base = eval_init_expr(&init_expr)?; + + let data_init = DataInitializer { + memory_index, + base, + data: data.to_vec(), + }; + + info.data_initializers.push(data_init); + } + DataKind::Passive => { + return Err(BinaryReaderError { + message: "passive memories are not yet supported", + offset: -1isize as usize, + }); + } + } + } + } + SectionCode::DataCount => {} + SectionCode::Custom { .. } => {} + } + } +} + +pub fn type_to_type(ty: WpType) -> Result { + Ok(match ty { + WpType::I32 => Type::I32, + WpType::I64 => Type::I64, + WpType::F32 => Type::F32, + WpType::F64 => Type::F64, + WpType::V128 => { + return Err(BinaryReaderError { + message: "the wasmer llvm backend does not yet support the simd extension", + offset: -1isize as usize, + }); + } + _ => panic!("broken invariant, invalid type"), + }) +} + +fn func_type_to_func_sig(func_ty: FuncType) -> Result { + assert_eq!(func_ty.form, WpType::Func); + + Ok(FuncSig::new( + func_ty + .params + .iter() + .cloned() + .map(type_to_type) + .collect::, _>>()?, + func_ty + .returns + .iter() + .cloned() + .map(type_to_type) + .collect::, _>>()?, + )) +} + +fn eval_init_expr(expr: &InitExpr) -> Result { + let mut reader = expr.get_operators_reader(); + let (op, offset) = reader.read_with_offset()?; + Ok(match op { + Operator::GetGlobal { global_index } => { + Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) + } + Operator::I32Const { value } => Initializer::Const(Value::I32(value)), + Operator::I64Const { value } => Initializer::Const(Value::I64(value)), + Operator::F32Const { value } => { + Initializer::Const(Value::F32(f32::from_bits(value.bits()))) + } + Operator::F64Const { value } => { + Initializer::Const(Value::F64(f64::from_bits(value.bits()))) + } + _ => { + return Err(BinaryReaderError { + message: "init expr evaluation failed: unsupported opcode", + offset, + }); + } + }) +} diff --git a/lib/llvm-backend/src/state.rs b/lib/llvm-backend/src/state.rs new file mode 100644 index 000000000..38b1178ce --- /dev/null +++ b/lib/llvm-backend/src/state.rs @@ -0,0 +1,105 @@ +use inkwell::{ + basic_block::BasicBlock, + values::{BasicValue, BasicValueEnum}, +}; +use wasmparser::BinaryReaderError; + +enum ControlFrame { + If { + dest: BasicBlock, + stack_size_snapshot: usize, + }, + Block { + dest: BasicBlock, + stack_size_snapshot: usize, + num_ret_values: usize, + }, +} + +pub struct State { + stack: Vec, + control_stack: Vec, + value_counter: usize, +} + +impl State { + pub fn new() -> Self { + Self { + stack: vec![], + control_stack: vec![], + value_counter: 0, + } + } + + pub fn var_name(&mut self) -> String { + let s = self.value_counter.to_string(); + self.value_counter += 1; + s + } + + pub fn push1(&mut self, value: T) { + self.stack.push(value.as_basic_value_enum()) + } + + pub fn pop1(&mut self) -> Result { + self.stack.pop().ok_or_else(|| BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + } + + pub fn pop2(&mut self) -> Result<(BasicValueEnum, BasicValueEnum), BinaryReaderError> { + let v2 = self.pop1()?; + let v1 = self.pop1()?; + Ok((v1, v2)) + } + + pub fn pop3( + &mut self, + ) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> { + let v3 = self.pop1()?; + let v2 = self.pop1()?; + let v1 = self.pop1()?; + Ok((v1, v2, v3)) + } + + pub fn peek1(&self) -> Result { + self.stack + .get(self.stack.len() - 1) + .ok_or_else(|| BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + .map(|v| *v) + } + + pub fn peekn(&self, n: usize) -> Result<&[BasicValueEnum], BinaryReaderError> { + self.stack + .get(self.stack.len() - n..) + .ok_or_else(|| BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + } + + pub fn popn(&mut self, n: usize) -> Result<(), BinaryReaderError> { + if self.stack.len() < n { + return Err(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }); + } + + let new_len = self.stack.len() - n; + self.stack.truncate(new_len); + Ok(()) + } + + pub fn push_block(&mut self, dest: BasicBlock, num_ret_values: usize) { + self.control_stack.push(ControlFrame::Block { + dest, + stack_size_snapshot: self.stack.len(), + num_ret_values, + }); + } +} diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 46f1fe982..9734f0c02 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -91,7 +91,7 @@ impl LocalBacking { } } as usize; - match init.memory_index.local_or_import(module) { + match init.memory_index.local_or_import(&module.info) { LocalOrImport::Local(local_memory_index) => { let memory_desc = module.info.memories[local_memory_index]; let data_top = init_base + init.data.len(); @@ -159,7 +159,7 @@ impl LocalBacking { } } as usize; - match init.table_index.local_or_import(module) { + match init.table_index.local_or_import(&module.info) { LocalOrImport::Local(local_table_index) => { let table = &tables[local_table_index]; @@ -178,7 +178,7 @@ impl LocalBacking { let sig_id = vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); - let (func, ctx) = match func_index.local_or_import(module) { + let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver @@ -217,7 +217,7 @@ impl LocalBacking { let sig_id = vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); - let (func, ctx) = match func_index.local_or_import(module) { + let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver @@ -366,7 +366,7 @@ fn import_functions( }, ) in &module.info.imported_functions { - let sig_index = module.info.func_assoc[index.convert_up(module)]; + let sig_index = module.info.func_assoc[index.convert_up(&module.info)]; let expected_sig = &module.info.signatures[sig_index]; let namespace = module.info.namespace_table.get(*namespace_index); diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 8255c125d..6a4d5d438 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -123,14 +123,14 @@ impl Instance { })?; } - let ctx = match func_index.local_or_import(&*self.module) { + let ctx = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => self.inner.vmctx, LocalOrImport::Import(imported_func_index) => { self.inner.import_backing.vm_functions[imported_func_index].vmctx } }; - let func_ptr = match func_index.local_or_import(&self.module) { + let func_ptr = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module .func_resolver @@ -291,7 +291,7 @@ impl Instance { })? } - let vmctx = match func_index.local_or_import(&self.module) { + let vmctx = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => self.inner.vmctx, LocalOrImport::Import(imported_func_index) => { self.inner.import_backing.vm_functions[imported_func_index].vmctx @@ -358,7 +358,7 @@ impl InstanceInner { .get(func_index) .expect("broken invariant, incorrect func index"); - let (func_ptr, ctx) = match func_index.local_or_import(module) { + let (func_ptr, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver @@ -384,7 +384,7 @@ impl InstanceInner { } fn get_memory_from_index(&self, module: &ModuleInner, mem_index: MemoryIndex) -> Memory { - match mem_index.local_or_import(module) { + match mem_index.local_or_import(&module.info) { LocalOrImport::Local(local_mem_index) => self.backing.memories[local_mem_index].clone(), LocalOrImport::Import(imported_mem_index) => { self.import_backing.memories[imported_mem_index].clone() @@ -393,7 +393,7 @@ impl InstanceInner { } fn get_global_from_index(&self, module: &ModuleInner, global_index: GlobalIndex) -> Global { - match global_index.local_or_import(module) { + match global_index.local_or_import(&module.info) { LocalOrImport::Local(local_global_index) => { self.backing.globals[local_global_index].clone() } @@ -404,7 +404,7 @@ impl InstanceInner { } fn get_table_from_index(&self, module: &ModuleInner, table_index: TableIndex) -> Table { - match table_index.local_or_import(module) { + match table_index.local_or_import(&module.info) { LocalOrImport::Local(local_table_index) => { self.backing.tables[local_table_index].clone() } @@ -462,7 +462,7 @@ impl<'a> DynFunc<'a> { })? } - let vmctx = match self.func_index.local_or_import(self.module) { + let vmctx = match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => self.instance_inner.vmctx, LocalOrImport::Import(imported_func_index) => { self.instance_inner.import_backing.vm_functions[imported_func_index].vmctx @@ -488,7 +488,7 @@ impl<'a> DynFunc<'a> { } pub fn raw(&self) -> *const vm::Func { - match self.func_index.local_or_import(self.module) { + match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module .func_resolver diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index c6b3f0a2b..bec1500b6 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -1,4 +1,9 @@ -use crate::{memory::MemoryType, module::ModuleInner, structures::TypedIndex, units::Pages}; +use crate::{ + memory::MemoryType, + module::{ModuleInfo, ModuleInner}, + structures::TypedIndex, + units::Pages, +}; use std::{borrow::Cow, mem}; /// Represents a WebAssembly type. @@ -340,23 +345,23 @@ define_map_index![ macro_rules! define_local_or_import { ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => { impl $ty { - pub fn local_or_import(self, module: &ModuleInner) -> LocalOrImport<$ty> { - if self.index() < module.info.$imports.len() { + pub fn local_or_import(self, info: &ModuleInfo) -> LocalOrImport<$ty> { + if self.index() < info.$imports.len() { LocalOrImport::Import(::Import::new(self.index())) } else { - LocalOrImport::Local(::Local::new(self.index() - module.info.$imports.len())) + LocalOrImport::Local(::Local::new(self.index() - info.$imports.len())) } } } impl $local_ty { - pub fn convert_up(self, module: &ModuleInner) -> $ty { - $ty ((self.index() + module.info.$imports.len()) as u32) + pub fn convert_up(self, info: &ModuleInfo) -> $ty { + $ty ((self.index() + info.$imports.len()) as u32) } } impl $imported_ty { - pub fn convert_up(self, _module: &ModuleInner) -> $ty { + pub fn convert_up(self, _module: &ModuleInfo) -> $ty { $ty (self.index() as u32) } } diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 8cb9f0986..eaa76fd22 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -116,7 +116,7 @@ impl Ctx { pub fn memory(&self, mem_index: u32) -> &Memory { let module = unsafe { &*self.module }; let mem_index = MemoryIndex::new(mem_index as usize); - match mem_index.local_or_import(module) { + match mem_index.local_or_import(&module.info) { LocalOrImport::Local(local_mem_index) => unsafe { let local_backing = &*self.local_backing; &local_backing.memories[local_mem_index]