diff --git a/Cargo.lock b/Cargo.lock index f3820ae53b53..e0cc0a3aef2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,18 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" +checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" dependencies = [ - "gimli", + "gimli 0.22.0", ] [[package]] -name = "adler32" -version = "1.1.0" +name = "adler" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" [[package]] name = "ahash" @@ -32,9 +32,9 @@ checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" [[package]] name = "aho-corasick" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" dependencies = [ "memchr", ] @@ -48,17 +48,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" +checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] name = "arbitrary" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5eb01a9ab8a3369f2f7632b9461c34f5920bd454774bab5b9fc6744f21d6143" +checksum = "0922a3e746b5a44e111e5603feb6704e5cc959116f66737f50bb5cbd264e9d87" dependencies = [ "derive_arbitrary", ] @@ -94,15 +103,15 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "autocfg" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c" +checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" dependencies = [ "addr2line", "cfg-if", @@ -114,15 +123,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - -[[package]] -name = "base64" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "binaryen" @@ -147,9 +150,9 @@ dependencies = [ [[package]] name = "bincode" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" dependencies = [ "byteorder", "serde", @@ -246,9 +249,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.54" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" +checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" dependencies = [ "jobserver", ] @@ -261,9 +264,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chrono" -version = "0.4.11" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" dependencies = [ "num-integer", "num-traits", @@ -272,11 +275,11 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.1" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ - "ansi_term", + "ansi_term 0.11.0", "atty", "bitflags", "strsim", @@ -305,9 +308,9 @@ dependencies = [ [[package]] name = "console" -version = "0.11.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c0994e656bba7b922d8dd1245db90672ffb701e684e45be58f20719d69abc5a" +checksum = "c0b1aacfaffdbff75be81c15a399b4bedf78aaefe840e8af1d299ac2ade885d2" dependencies = [ "encode_unicode", "lazy_static", @@ -381,7 +384,7 @@ dependencies = [ "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", - "gimli", + "gimli 0.21.0", "hashbrown 0.7.2", "log", "peepmatic", @@ -437,7 +440,7 @@ dependencies = [ "cranelift-reader", "file-per-thread-logger", "filecheck", - "gimli", + "gimli 0.21.0", "log", "memmap", "num_cpus", @@ -608,6 +611,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + [[package]] name = "crossbeam-deque" version = "0.7.3" @@ -625,7 +638,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", "cfg-if", "crossbeam-utils", "lazy_static", @@ -634,24 +647,13 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "maybe-uninit", -] - [[package]] name = "crossbeam-utils" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", "cfg-if", "lazy_static", ] @@ -667,9 +669,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee758ebd1c79a9c6fb95f242dcc30bdbf555c28369ae908d21fdaf81537496" +checksum = "d0f7c6c81276b6b8702074defbdb1938933ddf98c7f7e0dca8d9e9214dd6c730" dependencies = [ "proc-macro2", "quote", @@ -678,9 +680,9 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.7" +version = "0.99.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d" +checksum = "1dcfabdab475c16a93d669dddfc393027803e347d09663f524447f642fbb84ba" dependencies = [ "proc-macro2", "quote", @@ -771,9 +773,9 @@ dependencies = [ [[package]] name = "either" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" [[package]] name = "encode_unicode" @@ -796,9 +798,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b480f641ccf0faf324e20c1d3e53d81b7484c698b42ea677f6907ae4db195371" +checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01" dependencies = [ "errno-dragonfly", "libc", @@ -852,9 +854,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "file-per-thread-logger" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3937f028664bd0e13df401ba49a4567ccda587420365823242977f06609ed1" +checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126" dependencies = [ "env_logger", "log", @@ -872,9 +874,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" +checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" dependencies = [ "cfg-if", "libc", @@ -917,13 +919,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -937,6 +939,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" + [[package]] name = "glob" version = "0.3.0" @@ -971,9 +979,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" dependencies = [ "ahash 0.3.8", - "autocfg 1.0.0", + "autocfg 1.0.1", ] +[[package]] +name = "hashbrown" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" + [[package]] name = "heck" version = "0.3.1" @@ -985,9 +999,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" dependencies = [ "libc", ] @@ -1003,11 +1017,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", + "hashbrown 0.9.0", ] [[package]] @@ -1053,9 +1068,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "ittapi-rs" @@ -1089,15 +1104,15 @@ checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" [[package]] name = "libc" -version = "0.2.71" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "libfuzzer-sys" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d718794b8e23533b9069bd2c4597d69e41cc7ab1c02700a502971aca0cdcf24" +checksum = "ee8c42ab62f43795ed77a965ed07994c5584cdc94fd0ebf14b22ac1524077acc" dependencies = [ "arbitrary", "cc", @@ -1119,19 +1134,16 @@ dependencies = [ "memoffset", "more-asserts", "quickcheck", - "smallvec", - "staticvec", "thiserror", - "typemap", "wasmparser 0.57.0", "wat", ] [[package]] name = "log" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", ] @@ -1178,20 +1190,21 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", ] [[package]] name = "miniz_oxide" -version = "0.3.7" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9" dependencies = [ - "adler32", + "adler", + "autocfg 1.0.1", ] [[package]] @@ -1206,7 +1219,7 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", "num-traits", ] @@ -1216,7 +1229,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", ] [[package]] @@ -1253,9 +1266,9 @@ checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" [[package]] name = "once_cell" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" [[package]] name = "opaque-debug" @@ -1360,9 +1373,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "ppv-lite86" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" [[package]] name = "pretty_env_logger" @@ -1376,9 +1389,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -1389,28 +1402,26 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "syn", - "syn-mid", "version_check", ] [[package]] name = "proc-macro-hack" -version = "0.5.16" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" +checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" dependencies = [ "unicode-xid", ] @@ -1632,11 +1643,11 @@ dependencies = [ [[package]] name = "rayon" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" +checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", "crossbeam-deque", "either", "rayon-core", @@ -1644,12 +1655,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" +checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0" dependencies = [ + "crossbeam-channel", "crossbeam-deque", - "crossbeam-queue", "crossbeam-utils", "lazy_static", "num_cpus", @@ -1666,15 +1677,15 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_users" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" dependencies = [ "getrandom", "redox_syscall", @@ -1751,11 +1762,11 @@ dependencies = [ [[package]] name = "rust-argon2" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" dependencies = [ - "base64 0.11.0", + "base64", "blake2b_simd", "constant_time_eq", "crossbeam-utils", @@ -1852,18 +1863,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.112" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.112" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57" +checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" dependencies = [ "proc-macro2", "quote", @@ -1872,9 +1883,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.55" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" dependencies = [ "itoa", "ryu", @@ -1904,21 +1915,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" [[package]] name = "stable_deref_trait" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" - -[[package]] -name = "staticvec" -version = "0.10.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c0968d58741ac061880be1ca69c5ca6f27a1942ed456f08fd987e9c9013cead" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "string-interner" @@ -1937,9 +1942,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.14" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" +checksum = "6cc388d94ffabf39b5ed5fadddc40147cb21e605f53db6f8f36a625d27489ac5" dependencies = [ "clap", "lazy_static", @@ -1948,9 +1953,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" +checksum = "5e2513111825077552a6751dfad9e11ce0fba07d7276a3943a037d7e93e64c5f" dependencies = [ "heck", "proc-macro-error", @@ -1961,26 +1966,15 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.31" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] -[[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "target-lexicon" version = "0.10.0" @@ -2022,9 +2016,9 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8038f95fc7a6f351163f4b964af631bd26c9e828f7db085f2a84aca56f70d13b" +checksum = "9a14cd9f8c72704232f0bfc8455c0e861f0ad4eb60cc9ec8a170e231414c1e13" dependencies = [ "libc", "winapi", @@ -2066,18 +2060,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" dependencies = [ "proc-macro2", "quote", @@ -2095,11 +2089,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -2114,9 +2109,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.15" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41f40ed0e162c911ac6fcb53ecdc8134c46905fdbbae8c50add462a538b495f" +checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c" dependencies = [ "cfg-if", "log", @@ -2126,9 +2121,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bbad0de3fd923c9c3232ead88510b783e5a4d16a6154adffa3d53308de984c" +checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" dependencies = [ "proc-macro2", "quote", @@ -2137,9 +2132,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.10" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" +checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a" dependencies = [ "lazy_static", ] @@ -2157,9 +2152,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ccba2f8f16e0ed268fc765d9b7ff22e965e7185d32f8f1ec8294fe17d86e79" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" dependencies = [ "serde", "tracing-core", @@ -2167,11 +2162,11 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.5" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d53c40489aa69c9aed21ff483f26886ca8403df33bdc2d2f87c60c1617826d2" +checksum = "82bb5079aa76438620837198db8a5c529fb9878c730bc2b28179b0241cf04c10" dependencies = [ - "ansi_term", + "ansi_term 0.12.1", "chrono", "lazy_static", "matchers", @@ -2180,26 +2175,12 @@ dependencies = [ "serde_json", "sharded-slab", "smallvec", + "thread_local", "tracing-core", "tracing-log", "tracing-serde", ] -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" - -[[package]] -name = "typemap" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" -dependencies = [ - "unsafe-any", -] - [[package]] name = "typenum" version = "1.12.0" @@ -2214,24 +2195,15 @@ checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" - -[[package]] -name = "unsafe-any" -version = "0.4.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" -dependencies = [ - "traitobject", -] +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vec_map" @@ -2271,6 +2243,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi-common" version = "0.18.0" @@ -2293,24 +2271,24 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.55.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af931e2e1960c53f4a28b063fec4cacd036f35acbec8ff3a4739125b17382a87" +checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6" [[package]] name = "wasmparser" -version = "0.57.0" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6" +checksum = "e36b5b8441a5d83ea606c9eb904a3ee3889ebfeda1df1a5c48b84725239d93ce" [[package]] name = "wasmprinter" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c93ba310101ec5ee980db66b47b3d276577c8310df1570e19994347137650454" +checksum = "adc9e10f7145e1c15f16c809d6c0937ab51a79478f53458fb78ded3491819a94" dependencies = [ "anyhow", - "wasmparser 0.55.0", + "wasmparser 0.62.0", ] [[package]] @@ -2320,6 +2298,7 @@ dependencies = [ "anyhow", "backtrace", "cfg-if", + "itertools 0.9.0", "lazy_static", "libc", "log", @@ -2387,6 +2366,7 @@ dependencies = [ "wasmtime-runtime", "wasmtime-wasi", "wasmtime-wast", + "wast 14.0.0", "wat", ] @@ -2395,7 +2375,7 @@ name = "wasmtime-debug" version = "0.18.0" dependencies = [ "anyhow", - "gimli", + "gimli 0.21.0", "more-asserts", "object 0.19.0", "target-lexicon", @@ -2409,7 +2389,7 @@ name = "wasmtime-environ" version = "0.18.0" dependencies = [ "anyhow", - "base64 0.12.1", + "base64", "bincode", "cranelift-codegen", "cranelift-entity", @@ -2478,7 +2458,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli", + "gimli 0.21.0", "log", "more-asserts", "region", @@ -2508,7 +2488,7 @@ version = "0.18.0" dependencies = [ "anyhow", "cfg-if", - "gimli", + "gimli 0.21.0", "ittapi-rs", "lazy_static", "libc", @@ -2611,6 +2591,15 @@ dependencies = [ "leb128", ] +[[package]] +name = "wast" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b11c94c63d5365a76ea287f8e6e5b6050233fae4b2423aea2a1e126a385e17" +dependencies = [ + "leb128", +] + [[package]] name = "wast" version = "15.0.0" @@ -2631,20 +2620,20 @@ dependencies = [ [[package]] name = "wast" -version = "18.0.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b1f23531740a81f9300bd2febd397a95c76bfa4aa4bfaf4ca8b1ee3438f337" +checksum = "6ff1e3bd3ad0b2ee7784add89c30dc96b89a54b43e5d6d95d774eda1863b3500" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.19" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4006d418d59293172aebfeeadb7673459dc151874a79135946ea7996b6a98515" +checksum = "c7c0bb2872ae453f98cec6ff1bf1a71cde1da6041fce8b0ac39d51eb033e9ec0" dependencies = [ - "wast 18.0.0", + "wast 24.0.0", ] [[package]] @@ -2706,9 +2695,9 @@ dependencies = [ [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -2781,9 +2770,9 @@ dependencies = [ [[package]] name = "z3-sys" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4002d8a1facb54d02dbfb86151281e5450618ab330936bc2f3acaab31eae11ae" +checksum = "e1863cafae8eb86dd7d69c9218421b288594e8836346e93d4f36ade427195a21" dependencies = [ "cmake", ] diff --git a/Cargo.toml b/Cargo.toml index b9255545e164..3c68966b9512 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,12 +50,15 @@ tempfile = "3.1.0" test-programs = { path = "crates/test-programs" } wasmtime-fuzzing = { path = "crates/fuzzing" } wasmtime-runtime = { path = "crates/runtime" } +wast = "14.0.0" [build-dependencies] anyhow = "1.0.19" -[profile.release.build-override] -opt-level = 0 +[profile.release] +lto = true +codegen-units = 1 +panic = "abort" [workspace] members = [ @@ -73,6 +76,7 @@ members = [ [features] default = ["jitdump", "wasmtime/wat"] +benches = [] lightbeam = [ "wasmtime-environ/lightbeam", "wasmtime-jit/lightbeam", diff --git a/benches/main.rs b/benches/main.rs new file mode 100644 index 000000000000..af935b95a730 --- /dev/null +++ b/benches/main.rs @@ -0,0 +1,278 @@ +#![cfg(feature = "benches")] +#![feature(test)] + +extern crate test; + +use anyhow::{bail, Context as _, Result}; +use std::path::Path; +use wasmtime::{Config, Engine, OptLevel, Store, Strategy}; +use wasmtime_wast::WastContext; +use wast::{ + parser::{self, ParseBuffer}, + Wat, +}; + +macro_rules! mk_test { + ($(#[$attrs:meta])* $name:ident, $path:expr, $strategy:expr) => { + mod $name { + use wasmtime::Strategy; + + #[bench] + $(#[$attrs])* + fn compile(b: &mut ::test::bench::Bencher) { + crate::bench_compile(b, $path, $strategy).unwrap(); + } + + #[bench] + $(#[$attrs])* + fn run(b: &mut ::test::bench::Bencher) { + crate::bench_run(b, $path, $strategy).unwrap(); + } + } + }; +} + +include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs")); + +fn bench_compile(b: &mut test::bench::Bencher, wast: &str, strategy: Strategy) -> Result<()> { + let path = Path::new(wast); + + let simd = path.iter().any(|s| s == "simd"); + + let bulk_mem = path.iter().any(|s| s == "bulk-memory-operations"); + + // Some simd tests assume support for multiple tables, which are introduced + // by reference types. + let reftypes = simd || path.iter().any(|s| s == "reference-types"); + + let multi_val = path.iter().any(|s| s == "multi-value"); + + let mut cfg = Config::new(); + cfg.wasm_simd(simd) + .wasm_bulk_memory(bulk_mem) + .wasm_reference_types(reftypes) + .wasm_multi_value(multi_val) + .strategy(strategy)? + .cranelift_debug_verifier(cfg!(debug_assertions)); + + // FIXME: https://github.com/bytecodealliance/wasmtime/issues/1186 + if simd { + cfg.cranelift_opt_level(OptLevel::None); + } + + let store = Store::new(&Engine::new(&cfg)); + let mut wast_context = WastContext::new(store); + wast_context.register_spectest()?; + + let adjust_wast = |mut err: wast::Error| { + err.set_path(path); + err.set_text(wast); + err + }; + + let file_contents = std::fs::read_to_string(path) + .with_context(|| format!("failed to read `{}`", path.display()))?; + let buf = ParseBuffer::new(&file_contents).map_err(adjust_wast)?; + let ast = parser::parse::(&buf).map_err(adjust_wast)?; + + let mut modules = Vec::new(); + + for directive in ast.directives { + use wast::WastDirective::*; + + match directive { + Module(mut module) => { + let binary = module.encode()?; + + wast_context.module( + module.id.map(|s| &s.name()[..]).as_ref().map(|s| &s[..]), + &binary, + )?; + + b.bytes += binary.len() as u64; + modules.push(binary); + } + QuoteModule { span: _, source } => { + let mut module = String::new(); + for src in source { + module.push_str(std::str::from_utf8(src)?); + module.push_str(" "); + } + let buf = ParseBuffer::new(&module)?; + let mut wat = parser::parse::(&buf)?; + let binary = wat.module.encode()?; + wast_context.module( + wat.module + .id + .map(|s| &s.name()[..]) + .as_ref() + .map(|s| &s[..]), + &binary, + )?; + + b.bytes += binary.len() as u64; + modules.push(binary); + } + Register { + span: _, + name, + module, + } => { + wast_context.register(module.map(|s| s.name()), name)?; + } + _ => {} + } + } + + b.iter(|| { + for bin in &modules { + wast_context.instantiate(bin).unwrap(); + } + }); + + Ok(()) +} + +fn bench_run(b: &mut test::bench::Bencher, wast: &str, strategy: Strategy) -> Result<()> { + let path = Path::new(wast); + + let simd = path.iter().any(|s| s == "simd"); + + let bulk_mem = path.iter().any(|s| s == "bulk-memory-operations"); + + // Some simd tests assume support for multiple tables, which are introduced + // by reference types. + let reftypes = simd || path.iter().any(|s| s == "reference-types"); + + let multi_val = path.iter().any(|s| s == "multi-value"); + + let mut cfg = Config::new(); + cfg.wasm_simd(simd) + .wasm_bulk_memory(bulk_mem) + .wasm_reference_types(reftypes) + .wasm_multi_value(multi_val) + .strategy(strategy)? + .cranelift_debug_verifier(cfg!(debug_assertions)); + + // FIXME: https://github.com/bytecodealliance/wasmtime/issues/1186 + if simd { + cfg.cranelift_opt_level(OptLevel::None); + } + + let store = Store::new(&Engine::new(&cfg)); + let mut wast_context = WastContext::new(store); + wast_context.register_spectest()?; + + let adjust_wast = |mut err: wast::Error| { + err.set_path(path); + err.set_text(wast); + err + }; + + let file_contents = std::fs::read_to_string(path) + .with_context(|| format!("failed to read `{}`", path.display()))?; + let buf = ParseBuffer::new(&file_contents).map_err(adjust_wast)?; + let ast = parser::parse::(&buf).map_err(adjust_wast)?; + + let mut execute_directives = Vec::new(); + let mut current = None; + let mut dummy_name_id = 0; + + for directive in ast.directives { + use wast::{WastDirective::*, WastExecute}; + + match directive { + Module(mut module) => { + let binary = module.encode()?; + + let name = module.id.map(|s| s.name().to_string()).unwrap_or_else(|| { + let name = format!("---dummy-name-{}", dummy_name_id); + dummy_name_id += 1; + name + }); + wast_context.module(Some(&name), &binary)?; + current = Some(name); + } + QuoteModule { span: _, source } => { + let mut module = String::new(); + for src in source { + module.push_str(std::str::from_utf8(src)?); + module.push_str(" "); + } + let buf = ParseBuffer::new(&module)?; + let mut wat = parser::parse::(&buf)?; + let binary = wat.module.encode()?; + let name = wat + .module + .id + .map(|s| s.name().to_string()) + .unwrap_or_else(|| { + let name = format!("---dummy-name-{}", dummy_name_id); + dummy_name_id += 1; + name + }); + wast_context.module(Some(&name), &binary)?; + current = Some(name); + } + Register { + span: _, + name, + module, + } => { + wast_context.register(module.map(|s| s.name()), name)?; + } + Invoke(call) + | AssertExhaustion { call, .. } + | AssertReturn { + exec: WastExecute::Invoke(call), + .. + } + | AssertTrap { + exec: WastExecute::Invoke(call), + .. + } => { + use wasmtime::Val; + + // Copy/pasted from `wasmtime-wast` + fn runtime_value(v: &wast::Expression<'_>) -> Result { + use wast::Instruction::*; + + if v.instrs.len() != 1 { + bail!("too many instructions in {:?}", v); + } + Ok(match &v.instrs[0] { + I32Const(x) => Val::I32(*x), + I64Const(x) => Val::I64(*x), + F32Const(x) => Val::F32(x.bits), + F64Const(x) => Val::F64(x.bits), + V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())), + other => bail!("couldn't convert {:?} to a runtime value", other), + }) + } + + let values = call + .args + .iter() + .map(runtime_value) + .collect::>>()?; + + execute_directives.push(( + call.module + .map(|m| m.name().to_string()) + .unwrap_or_else(|| current.clone().unwrap()), + call.name.to_string(), + values, + )); + } + _ => {} + } + } + + b.iter(|| { + for (mod_name, fn_name, args) in &execute_directives { + wast_context.invoke(Some(mod_name), fn_name, args).unwrap(); + } + }); + + Ok(()) +} diff --git a/build.rs b/build.rs index 8b3374b48870..f6083b6dc68f 100644 --- a/build.rs +++ b/build.rs @@ -22,7 +22,6 @@ fn main() -> anyhow::Result<()> { #[cfg(feature = "lightbeam")] "Lightbeam", ] { - writeln!(out, "#[cfg(test)]")?; writeln!(out, "#[allow(non_snake_case)]")?; writeln!(out, "mod {} {{", strategy)?; @@ -150,15 +149,14 @@ fn write_testsuite_tests( let path = path.as_ref(); let testname = extract_name(path); - writeln!(out, "#[test]")?; + writeln!(out, "mk_test! {{")?; if ignore(testsuite, &testname, strategy) { writeln!(out, "#[ignore]")?; } - writeln!(out, "fn r#{}() {{", &testname)?; - writeln!(out, " let _ = env_logger::try_init();")?; writeln!( out, - " crate::wast::run_wast(r#\"{}\"#, crate::wast::Strategy::{}).unwrap();", + "r#{}, r#\"{}\"#, Strategy::{}", + &testname, path.display(), strategy )?; diff --git a/crates/environ/src/address_map.rs b/crates/environ/src/address_map.rs index 461f550eb209..dfb457b98a4d 100644 --- a/crates/environ/src/address_map.rs +++ b/crates/environ/src/address_map.rs @@ -20,7 +20,7 @@ pub struct InstructionAddressMap { } /// Function and its instructions addresses mappings. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct FunctionAddressMap { /// Instructions maps. /// The array is sorted by the InstructionAddressMap::code_offset field. diff --git a/crates/environ/src/lightbeam.rs b/crates/environ/src/lightbeam.rs index 49fda2f8a023..1bfbc2ccd120 100644 --- a/crates/environ/src/lightbeam.rs +++ b/crates/environ/src/lightbeam.rs @@ -68,7 +68,7 @@ impl crate::compilation::Compiler for Lightbeam { .map_err(|e| CompileError::Codegen(format!("Failed to generate output code: {}", e)))?; // TODO pass jump table offsets to Compilation::from_buffer() when they - // are implemented in lightbeam -- using empty set of offsets for now. + // are implemented in lightbeam -- using empty set of offsets for now. // TODO: pass an empty range for the unwind information until lightbeam emits it let code_section_ranges_and_jt = code_section .funcs() diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 97b0ef05e12a..56e13a7480a2 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -140,7 +140,6 @@ impl CompiledModule { }; let finished_functions = FinishedFunctions(finished_functions.into_boxed_slice()); - Ok(Self { module: Arc::new(module), code: Arc::new(ModuleCode { diff --git a/crates/lightbeam/Cargo.toml b/crates/lightbeam/Cargo.toml index 5c25375a565d..dbecd5b41945 100644 --- a/crates/lightbeam/Cargo.toml +++ b/crates/lightbeam/Cargo.toml @@ -20,10 +20,7 @@ iter-enum = "0.2" itertools = "0.8.2" memoffset = "0.5.3" more-asserts = "0.2.1" -smallvec = "1.0.0" -staticvec = "0.10" thiserror = "1.0.9" -typemap = "0.3" wasmparser = "0.57.0" [dev-dependencies] diff --git a/crates/lightbeam/src/alloc.rs b/crates/lightbeam/src/alloc.rs new file mode 100644 index 000000000000..a17208d6dbba --- /dev/null +++ b/crates/lightbeam/src/alloc.rs @@ -0,0 +1,256 @@ +use std::{ + collections::{BTreeMap, HashSet}, + ops, +}; + +#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, Copy, Clone)] +pub struct Ptr(pub usize); + +impl Ptr { + fn offset(self, size: Size) -> Ptr { + Ptr(self.0 + size.0) + } +} + +#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, Copy, Clone)] +pub struct Size(pub usize); + +impl ops::Add for Size { + type Output = Self; + + fn add(self, other: Self) -> Self { + Size(self.0 + other.0) + } +} + +impl ops::Sub for Size { + type Output = Self; + + fn sub(self, other: Self) -> Self { + Size(self.0 - other.0) + } +} + +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Alloc { + size: Size, + blocks_by_address: BTreeMap, + blocks_by_size: BTreeMap>, +} + +impl Alloc { + pub fn new(bytes: Size) -> Self { + let mut out = Self::default(); + out.set_size(bytes); + out.free(Ptr(0), bytes); + out + } + + pub fn set_size(&mut self, size: Size) { + use std::cmp::Ordering; + match size.cmp(&self.size) { + Ordering::Less => self.truncate(size), + Ordering::Equal | Ordering::Greater => {} + } + + self.size = size; + } + + pub fn size(&self) -> Size { + self.size + } + + fn truncate(&mut self, size: Size) { + self.mark_allocated(Ptr(size.0), self.size - size); + } + + pub fn mark_allocated(&mut self, ptr: Ptr, size: Size) { + use std::cmp::Ordering; + + let end = ptr.offset(size); + + if let Some((&existing_ptr, &existing_size)) = self + .blocks_by_address + .range(..end) + .last() + .filter(|(p, s)| **p <= ptr && p.offset(**s) >= end) + { + match ptr.cmp(&existing_ptr) { + Ordering::Less => unreachable!(), + Ordering::Equal => { + self.remove_block(existing_ptr); + self.add_block(end, existing_size - size); + } + Ordering::Greater => { + let existing_end = existing_ptr.offset(existing_size); + + self.modify_block(existing_ptr, Size(ptr.0 - existing_ptr.0)); + self.add_block(end, Size(existing_end.0 - end.0)); + } + } + } + } + + pub fn malloc(&mut self, size: Size) -> Option { + use std::cmp::Ordering; + + let (&existing_size, ptrs) = self.blocks_by_size.range_mut(size..).next()?; + + let ptr = *ptrs.iter().next().expect("Allocator metadata corrupted"); + + self.remove_block(ptr); + + match existing_size.cmp(&size) { + Ordering::Less => unreachable!(), + Ordering::Equal => {} + Ordering::Greater => self.add_block(ptr.offset(size), existing_size - size), + } + + Some(ptr) + } + + pub fn free(&mut self, ptr: Ptr, size: Size) { + let prev_block = self + .blocks_by_address + .range(..=ptr) + .last() + .filter(|(p, s)| p.offset(**s) == ptr) + .map(|(p, s)| (*p, *s)); + let next_block = self + .blocks_by_address + .get_key_value(&ptr.offset(size)) + .map(|(p, s)| (*p, *s)); + + match (prev_block, next_block) { + (Some((prev_ptr, prev_size)), Some((next_ptr, next_size))) => { + self.remove_block(next_ptr); + self.modify_block(prev_ptr, prev_size + size + next_size); + } + (None, Some((next_ptr, next_size))) => { + self.remove_block(next_ptr); + self.add_block(ptr, size + next_size); + } + (Some((prev_ptr, prev_size)), None) => { + self.modify_block(prev_ptr, size + prev_size); + } + (None, None) => { + self.add_block(ptr, size); + } + } + } + + pub fn is_free(&self, ptr: Ptr, size: Size) -> bool { + self.blocks_by_address + .range(..=ptr) + .last() + .map(|(p, s)| p.offset(*s) >= ptr.offset(size)) + .unwrap_or(false) + } + + pub fn used_size(&self) -> Size { + if let Some((p, _)) = self + .blocks_by_address + .iter() + .last() + .filter(|(p, s)| p.offset(**s).0 == self.size.0) + { + Size(p.0) + } else { + self.size + } + } + + fn modify_block(&mut self, ptr: Ptr, new_size: Size) { + self.remove_block(ptr); + self.add_block(ptr, new_size); + } + + fn add_block(&mut self, ptr: Ptr, size: Size) { + if size.0 > 0 { + let existing = self.blocks_by_address.insert(ptr, size); + debug_assert!(existing.is_none()); + let existing = self.blocks_by_size.entry(size).or_default().insert(ptr); + debug_assert!(existing); + } + } + + fn remove_block(&mut self, ptr: Ptr) { + use std::collections::btree_map::Entry; + + let size = self + .blocks_by_address + .remove(&ptr) + .expect("Double-free'd block"); + match self.blocks_by_size.entry(size) { + Entry::Occupied(mut entry) => { + let existing = entry.get_mut().remove(&ptr); + debug_assert!(existing, "Allocator metadata corrupted"); + if entry.get().is_empty() { + entry.remove_entry(); + } + } + Entry::Vacant(_) => panic!("Allocator metadata corrupted"), + } + } +} + +#[cfg(test)] +mod tests { + use super::{Alloc, Ptr, Size}; + + #[test] + fn alloc_free() { + let small = Size(8); + let mid = Size(16); + let large = Size(32); + let mut alloc = Alloc::new(small + mid + large); + + let small = Size(8); + let mid = Size(16); + let large = Size(32); + let a = alloc.malloc(small).unwrap(); + let b = alloc.malloc(mid).unwrap(); + let c = alloc.malloc(large).unwrap(); + assert_eq!(a, Ptr(0)); + assert_eq!(b, Ptr(8)); + assert_eq!(c, Ptr(24)); + assert!(alloc.malloc(small).is_none()); + alloc.free(a, small); + alloc.free(b, mid); + let a = alloc.malloc(small).unwrap(); + let b1 = alloc.malloc(small).unwrap(); + let b2 = alloc.malloc(small).unwrap(); + assert!(alloc.malloc(small).is_none()); + assert_eq!(a, Ptr(0)); + assert_eq!(b1, Ptr(8)); + assert_eq!(b2, Ptr(16)); + } + + #[test] + fn mark_allocated() { + let small = Size(8); + let mid = Size(16); + let large = Size(32); + let mut alloc = Alloc::new(small + mid + large); + + let small = Size(8); + let mid = Size(16); + let large = Size(32); + let a = Ptr(0); + let b = Ptr(8); + alloc.mark_allocated(b, mid); + alloc.mark_allocated(a, small); + + println!("{:?}", alloc); + + alloc.free(a, small); + alloc.free(b, mid); + alloc.mark_allocated(Ptr(24), large); + let a = alloc.malloc(small).unwrap(); + let b1 = alloc.malloc(small).unwrap(); + let b2 = alloc.malloc(small).unwrap(); + assert_eq!(a, Ptr(0)); + assert_eq!(b1, Ptr(8)); + assert_eq!(b2, Ptr(16)); + } +} diff --git a/crates/lightbeam/src/backend.rs b/crates/lightbeam/src/backend.rs index 6be49873e12e..8a50e48ee38e 100644 --- a/crates/lightbeam/src/backend.rs +++ b/crates/lightbeam/src/backend.rs @@ -1,9 +1,13 @@ #![allow(clippy::float_cmp)] use self::registers::*; -use crate::error::Error; -use crate::microwasm::{BrTarget, Ieee32, Ieee64, SignlessType, Type, Value, F32, F64, I32, I64}; -use crate::module::ModuleContext; +use crate::{ + alloc::{Alloc, Ptr, Size}, + error::{error, Error}, + microwasm::{BrTarget, Ieee32, Ieee64, SignlessType, Type, Value, F32, F64, I32, I64}, + module::{ModuleContext, Signature}, + Sinks, +}; use cranelift_codegen::{ binemit, ir::{self, SourceLoc, TrapCode}, @@ -11,17 +15,21 @@ use cranelift_codegen::{ use dynasm::dynasm; use dynasmrt::x64::Assembler; use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer}; +use more_asserts::{assert_ge, assert_lt, debug_assert_ge, debug_assert_lt}; +#[cfg(debug_assertions)] +use more_asserts::{assert_le, debug_assert_le}; use std::{ - cmp::Ordering, + collections::HashMap, convert::{TryFrom, TryInto}, fmt::Display, hash::Hash, - iter, mem, + iter::{self, FromIterator}, + mem, ops::{Deref, RangeInclusive}, }; -// use wasmtime_environ::BuiltinFunctionIndex; mod magic { + // This is a replacement for `use wasmtime_environ::BuiltinFunctionIndex;` /// An index type for builtin functions. pub struct BuiltinFunctionIndex(u32); @@ -107,40 +115,71 @@ impl GPR { } } -fn arg_locs>( - types: I, -) -> impl ExactSizeIterator + DoubleEndedIterator + Clone +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Locs { + pub max_depth: StackDepth, + pub locs: I, +} + +impl Locs where - I::IntoIter: ExactSizeIterator + DoubleEndedIterator + Clone, + for<'a> &'a I::Target: IntoIterator, { + pub fn as_ref(&self) -> Locs::IntoIter>> { + Locs { + // We start and return the function with stack depth 1 since we must + // allow space for the saved return address. + max_depth: self.max_depth.clone(), + locs: self.locs.into_iter().copied(), + } + } +} + +fn locs, I: IntoIterator>( + types: I, + integers_in_gprs: &'static [GPR], + floats_in_gprs: &'static [GPR], +) -> Locs> { // TODO: VmCtx is in the first register - let mut int_gpr_iter = INTEGER_ARGS_IN_GPRS.iter(); - let mut float_gpr_iter = FLOAT_ARGS_IN_GPRS.iter(); - let mut stack_idx = 0; + let mut int_gpr_iter = integers_in_gprs.iter(); + let mut float_gpr_iter = floats_in_gprs.iter(); + let mut stack_idx = 0u32; - types + let locs = types .into_iter() - .map(move |ty| { + .map(|ty| { match ty { I32 | I64 => int_gpr_iter.next(), F32 | F64 => float_gpr_iter.next(), } .map(|&r| CCLoc::Reg(r)) .unwrap_or_else(|| { - let out = CCLoc::Stack(stack_idx); + let out = CCLoc::Stack(stack_idx as i32); stack_idx += 1; out }) }) + .map(From::from) // Since we only advance the iterators based on the values in `types`, // we can't do this lazily. - .collect::>() - .into_iter() + .collect::>(); + + Locs { + max_depth: StackDepth(stack_idx), + locs, + } +} + +fn arg_locs, I: IntoIterator>(types: I) -> Locs> +where + I::IntoIter: ExactSizeIterator + DoubleEndedIterator + Clone, +{ + locs::(types, INTEGER_ARGS_IN_GPRS, FLOAT_ARGS_IN_GPRS) } -fn arg_locs_skip_caller_vmctx>( +fn arg_locs_skip_caller_vmctx, I: IntoIterator>( types: I, -) -> impl ExactSizeIterator + DoubleEndedIterator + Clone +) -> Locs> where I::IntoIter: ExactSizeIterator + DoubleEndedIterator + Clone, { @@ -181,42 +220,30 @@ where impl ExactSizeIterator for WithInt where I: ExactSizeIterator {} - arg_locs(WithInt { + let Locs { + mut locs, + max_depth, + } = arg_locs::(WithInt { caller_vmctx_ty: Some(I32), iter: types.into_iter(), - }) - .skip(1) + }); + + locs.remove(0); + + Locs { locs, max_depth } } -pub fn ret_locs(types: impl IntoIterator) -> Result, Error> { - let types = types.into_iter(); - let mut out = Vec::with_capacity(types.size_hint().0); - // TODO: VmCtx is in the first register - let mut int_gpr_iter = INTEGER_RETURN_GPRS.iter(); - let mut float_gpr_iter = FLOAT_RETURN_GPRS.iter(); - - for ty in types { - match ty { - I32 | I64 => match int_gpr_iter.next() { - None => { - return Err(Error::Microwasm( - "We don't support stack returns yet".to_string(), - )) - } - Some(val) => out.push(CCLoc::Reg(*val)), - }, - F32 | F64 => match float_gpr_iter.next() { - None => { - return Err(Error::Microwasm( - "We don't support stack returns yet".to_string(), - )) - } - Some(val) => out.push(CCLoc::Reg(*val)), - }, - } +pub fn ret_locs, I: IntoIterator>(types: I) -> Locs> { + let out = locs::(types, INTEGER_RETURN_GPRS, FLOAT_RETURN_GPRS); + + if out.max_depth != StackDepth(0) { + unimplemented!( + "We need to implement proper MEMORY-type argument passing \ + in Lightbeam, by taking a hidden first argument and returning a pointer" + ); } - Ok(out) + out } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -398,7 +425,7 @@ impl Registers { let c = &mut scratch_counts.1[gpr as usize]; *c = match c.checked_sub(1) { Some(e) => e, - None => return Err(Error::Microwasm(format!("Double-freed register: {}", gpr))), + None => return Err(error(format!("Double-freed register: {}", gpr))), }; if *c == 0 { scratch_counts.0.release(gpr); @@ -413,32 +440,38 @@ impl Registers { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct BlockCallingConvention> { - pub stack_depth: StackDepth, - pub arguments: I, +pub struct CallingConvention { + pub arguments: Locs, + pub depth: Option, } -impl BlockCallingConvention { +// The return address and the saved `rbp` each take up a slot. +pub(crate) const FUNCTION_START_DEPTH: StackDepth = StackDepth(2); + +impl CallingConvention { pub fn function_start(arguments: I) -> Self { - BlockCallingConvention { - // We start and return the function with stack depth 1 since we must - // allow space for the saved return address. - stack_depth: StackDepth(1), - arguments, + CallingConvention { + depth: Some(FUNCTION_START_DEPTH), + arguments: Locs { + locs: arguments, + max_depth: FUNCTION_START_DEPTH, + }, } } } -impl BlockCallingConvention +impl CallingConvention where for<'a> &'a I::Target: IntoIterator, { - pub fn as_ref(&self) -> BlockCallingConvention + '_> { - BlockCallingConvention { + pub fn as_ref( + &self, + ) -> CallingConvention::IntoIter>> { + CallingConvention { // We start and return the function with stack depth 1 since we must // allow space for the saved return address. - stack_depth: self.stack_depth.clone(), - arguments: self.arguments.into_iter().copied(), + depth: self.depth.clone(), + arguments: self.arguments.as_ref(), } } } @@ -458,12 +491,30 @@ pub enum CCLoc { Stack(i32), } -impl CCLoc { - fn try_from(other: ValueLocation) -> Option { +impl TryFrom for CCLoc { + type Error = (); + + fn try_from(other: ValueLocation) -> Result { match other { - ValueLocation::Reg(reg) => Some(CCLoc::Reg(reg)), - ValueLocation::Stack(offset) => Some(CCLoc::Stack(offset)), - ValueLocation::Cond(_) | ValueLocation::Immediate(_) => None, + ValueLocation::Reg(reg) => Ok(CCLoc::Reg(reg)), + ValueLocation::Stack(offset) => Ok(CCLoc::Stack(offset)), + ValueLocation::Cond(_) | ValueLocation::Immediate(_) => Err(()), + } + } +} + +impl CCLoc { + pub fn stack(self) -> Option { + match self { + CCLoc::Stack(o) => Some(o), + _ => None, + } + } + + pub fn reg(self) -> Option { + match self { + CCLoc::Reg(r) => Some(r), + _ => None, } } } @@ -485,8 +536,8 @@ pub enum CondCode { mod cc { use super::CondCode; - pub const EQUAL: CondCode = CondCode::ZF0; - pub const NOT_EQUAL: CondCode = CondCode::ZF1; + pub const EQUAL: CondCode = CondCode::ZF1; + pub const NOT_EQUAL: CondCode = CondCode::ZF0; pub const GE_U: CondCode = CondCode::CF0; pub const LT_U: CondCode = CondCode::CF1; pub const GT_U: CondCode = CondCode::CF0AndZF0; @@ -542,21 +593,21 @@ impl From for ValueLocation { } impl ValueLocation { - fn stack(self) -> Option { + pub fn stack(self) -> Option { match self { ValueLocation::Stack(o) => Some(o), _ => None, } } - fn reg(self) -> Option { + pub fn reg(self) -> Option { match self { ValueLocation::Reg(r) => Some(r), _ => None, } } - fn immediate(self) -> Option { + pub fn immediate(self) -> Option { match self { ValueLocation::Immediate(i) => Some(i), _ => None, @@ -585,13 +636,13 @@ impl ValueLocation { // All rest arguments are passed on the stack. // Usually system-v uses rdi and rsi, but rdi is used for the vmctx and rsi is used for the _caller_ vmctx const INTEGER_ARGS_IN_GPRS: &[GPR] = &[GPR::Rq(CALLER_VMCTX), RDX, RCX, R8, R9]; -const INTEGER_RETURN_GPRS: &[GPR] = &[RAX, RDX]; +const INTEGER_RETURN_GPRS: &[GPR] = &[RAX, RDX, RCX]; const FLOAT_ARGS_IN_GPRS: &[GPR] = &[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]; const FLOAT_RETURN_GPRS: &[GPR] = &[XMM0, XMM1]; // List of scratch registers taken from https://wiki.osdev.org/System_V_ABI const SCRATCH_REGS: &[GPR] = &[ - RDX, RCX, R8, R9, RAX, R10, R11, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, - XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, + RSI, RDX, RCX, R8, R9, RAX, R10, R11, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, + XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, ]; const VMCTX: RegId = rq::RDI; const CALLER_VMCTX: RegId = rq::RSI; @@ -630,11 +681,11 @@ impl<'module, M> CodeGenSession<'module, M> { pub fn new_context<'this>( &'this mut self, - func_idx: u32, - reloc_sink: &'this mut dyn binemit::RelocSink, + defined_func_idx: u32, + sinks: Sinks<'this>, ) -> Context<'this, M> { { - let func_start = &mut self.func_starts[func_idx as usize]; + let func_start = &mut self.func_starts[defined_func_idx as usize]; // At this point we know the exact start address of this function. Save it // and define dynamic label at this location. @@ -644,12 +695,16 @@ impl<'module, M> CodeGenSession<'module, M> { Context { asm: &mut self.assembler, - current_function: func_idx, - reloc_sink, + current_function: defined_func_idx, + sinks, pointer_type: self.pointer_type, source_loc: Default::default(), func_starts: &self.func_starts, - block_state: Default::default(), + stack: Default::default(), + physical_stack_depth: StackDepth(0), + stack_depth_locked: false, + regs: Default::default(), + allocated_stack: Default::default(), module_context: self.module_context, labels: Default::default(), } @@ -725,11 +780,171 @@ impl TranslatedCodeSection { } } -#[derive(Debug, Default, Clone)] -pub struct BlockState { - pub stack: Stack, - pub depth: StackDepth, - pub regs: Registers, +#[derive(Debug, Default, Clone, PartialEq)] +pub struct StackUsage { + // This is so we can track usage of stack args + offset: u32, + alloc: Alloc, + // TODO: Make this `NonZeroU32` + num_usages: HashMap, +} + +const ELEMENT_SIZE: Size = Size(WORD_SIZE as usize); + +fn real_offset(num_arguments: u32, offset: i32) -> Result { + (-(offset + 1)) + .checked_add(num_arguments as i32) + .and_then(|o| u32::try_from(o).ok()) + .map(|o| o * WORD_SIZE) + .ok_or_else(|| { + error(format!( + "Tried to access stack value outside of bounds: {}, {}", + num_arguments, offset + )) + }) +} + +impl StackUsage { + pub fn new(num_arguments: u32) -> Self { + StackUsage { + offset: num_arguments, + alloc: Alloc::new(Size((num_arguments * WORD_SIZE) as usize)), + num_usages: Default::default(), + } + } + + pub fn clear(&mut self) { + *self = Self::new(self.offset); + } + + fn real_offset(&self, offset: i32) -> Result { + real_offset(self.offset, offset) + } + + pub fn alloc(&mut self) -> Option { + let ptr = self.alloc.malloc(ELEMENT_SIZE)?; + + let _prev_val = self.num_usages.insert(ptr, 1); + // debug_assert_eq!(prev_val, None); + + Some(-((ptr.0 as u32 / WORD_SIZE) as i32 - self.offset as i32) - 1) + } + + pub fn set_depth(&mut self, depth: StackDepth) -> Result<(), Error> { + let alloc_size = (depth.0 + self.offset) * WORD_SIZE; + let cur_depth = self.stack_depth(); + self.alloc.set_size(Size(alloc_size as usize)); + + for i in cur_depth.0..depth.0 { + let ptr = Ptr(self.real_offset(-(i as i32 + 1))? as usize); + let _prev_val = self.num_usages.insert(ptr, 1); + // debug_assert_eq!(prev_val, None); + } + + // for i in depth.0..cur_depth.0 { + // let ptr = Ptr(self.real_offset(-(i as i32 + 1))? as usize); + // let _prev_val = self.num_usages.remove(&ptr); + // // debug_assert_eq!(prev_val, None); + // } + + Ok(()) + } + + pub fn set_depth_and_free(&mut self, depth: StackDepth) -> Result<(), Error> { + let ptr = Ptr(self.alloc.size().0); + let cur_depth = self.stack_depth(); + + let alloc_size = (depth.0 + self.offset) * WORD_SIZE; + self.alloc.set_size(Size(alloc_size as usize)); + + if let Some(size) = depth + .0 + .checked_sub(cur_depth.0) + .map(|diff| diff * WORD_SIZE) + { + self.alloc.free(ptr, Size(size as usize)); + } + + // for i in depth.0..cur_depth.0 { + // let ptr = Ptr(self.real_offset(-(i as i32 + 1))? as usize); + // let _prev_val = self.num_usages.remove(&ptr); + // // debug_assert_eq!(prev_val, None); + // } + + Ok(()) + } + + pub fn mark_used(&mut self, offset: i32) -> Result<(), Error> { + let ptr = Ptr(self.real_offset(offset)? as usize); + if self.alloc.is_free(ptr, ELEMENT_SIZE) { + self.alloc.mark_allocated(ptr, ELEMENT_SIZE); + let _prev_val = self.num_usages.insert(ptr, 1); + // debug_assert_eq!(prev_val, None); + } else { + *self.num_usages.entry(ptr).or_insert(0) += 1; + } + + Ok(()) + } + + pub fn release(&mut self, offset: i32) -> Result<(), Error> { + let ptr = Ptr(self.real_offset(offset)? as usize); + + if self.alloc.is_free(ptr, ELEMENT_SIZE) { + return Err(error(format!( + "Tried to release stack element that was already free at {}\nState: {:#?}", + offset, self, + ))); + } + + let num_usages = self + .num_usages + .remove(&ptr) + .ok_or_else(|| { + error(format!( + "Tried to release stack element that was already free at {}", + offset + )) + })? + .checked_sub(1) + .ok_or_else(|| { + error(format!( + "Tried to release stack element that was already free at {}", + offset + )) + })?; + + if num_usages == 0 { + self.alloc.free(ptr, ELEMENT_SIZE); + } else { + self.num_usages.insert(ptr, num_usages); + } + + Ok(()) + } + + pub fn num_usages(&self, offset: i32) -> Result { + let ptr = Ptr(self.real_offset(offset)? as usize); + + Ok(*self + .num_usages + .get(&ptr) + .ok_or_else(|| error("Tried to get number of usages of element that doesn't exist"))?) + } + + pub fn is_free(&self, offset: i32) -> Result { + let ptr = Ptr(self.real_offset(offset)? as usize); + + Ok(self.alloc.is_free(ptr, ELEMENT_SIZE)) + } + + pub fn stack_depth(&self) -> StackDepth { + StackDepth((self.alloc.size().0 as u32 / WORD_SIZE) - self.offset) + } + + pub fn used_stack_depth(&self) -> StackDepth { + StackDepth((self.alloc.used_size().0 as u32 / WORD_SIZE) - self.offset) + } } type Stack = Vec; @@ -738,10 +953,9 @@ mod labels { use super::Label; use std::collections::HashMap; - pub struct LabelInfo { + pub struct UndefinedLabel { pub label: Label, pub align: u32, - pub inner: LabelValue, } #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -753,12 +967,21 @@ mod labels { #[derive(Default)] pub struct Labels { - map: HashMap, + map: HashMap>, } impl Labels { - pub fn drain(&mut self) -> impl Iterator + '_ { - self.map.drain().map(|(_, info)| info) + pub fn drain(&mut self) -> impl Iterator + '_ { + self.map.iter_mut().filter_map(|(val, info)| { + let label = match info { + Ok(info) => info.label, + Err(label) => *label, + }; + + std::mem::replace(info, Err(label)) + .ok() + .map(|info| (*val, info)) + }) } pub fn insert( @@ -767,31 +990,50 @@ mod labels { align: u32, label: LabelValue, ) -> Label { - let val = self.map.entry(label).or_insert_with(move || LabelInfo { - label: l(), - align, - inner: label, - }); + let val = self + .map + .entry(label) + .or_insert_with(move || Ok(UndefinedLabel { label: l(), align })); - val.align = val.align.max(align); + match val { + Ok(info) => { + info.align = info.align.max(align); - val.label + info.label + } + Err(label) => *label, + } + } + + pub fn define(&mut self, label: &LabelValue) -> Option> { + self.map.get_mut(label).map(|info| { + let label = match info { + Ok(info) => info.label, + Err(label) => *label, + }; + + std::mem::replace(info, Err(label)) + }) } } } -use labels::{LabelInfo, LabelValue, Labels}; +use labels::{LabelValue, Labels, UndefinedLabel}; pub struct Context<'this, M> { pub asm: &'this mut Assembler, pointer_type: SignlessType, source_loc: SourceLoc, - reloc_sink: &'this mut dyn binemit::RelocSink, + pub sinks: Sinks<'this>, module_context: &'this M, current_function: u32, func_starts: &'this Vec<(Option, DynamicLabel)>, - /// Each push and pop on the value stack increments or decrements this value by 1 respectively. - pub block_state: BlockState, + pub stack: Stack, + pub physical_stack_depth: StackDepth, + stack_depth_locked: bool, + pub regs: Registers, + // TODO: Replace with `alloc` + pub allocated_stack: StackUsage, labels: Labels, } @@ -803,16 +1045,6 @@ pub struct Label(DynamicLabel); #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct StackDepth(u32); -impl StackDepth { - pub fn reserve(&mut self, slots: u32) { - self.0 = self.0.checked_add(slots).unwrap(); - } - - pub fn free(&mut self, slots: u32) { - self.0 = self.0.checked_sub(slots).unwrap(); - } -} - macro_rules! int_div { ($full_div_s:ident, $full_div_u:ident, $div_u:ident, $div_s:ident, $rem_u:ident, $rem_s:ident, $imm_fn:ident, $signed_ty:ty, $unsigned_ty:ty, $reg_ty:tt, $pointer_ty:tt) => { // TODO: Fast div using mul for constant divisor? It looks like LLVM doesn't do that for us when @@ -834,29 +1066,39 @@ macro_rules! int_div { return Ok(()) } - let (div, rem, saved) = self.$full_div_u(divisor, dividend)?; + let (mut div, rem, saved) = self.$full_div_u(divisor, dividend)?; - self.free_value(rem)?; + self.free(rem)?; - let div = match div { - ValueLocation::Reg(div) => { - if saved.clone().any(|dst| dst == div) { - let new = self.take_reg(I32).unwrap(); - dynasm!(self.asm - ; mov Rq(new.rq().unwrap()), Rq(div.rq().unwrap()) - ); - self.block_state.regs.release(div)?; - ValueLocation::Reg(new) - } else { - ValueLocation::Reg(div) - } - } + match div { + ValueLocation::Reg(div_reg) + if saved.clone().any(|(_, dst)| ValueLocation::from(dst) == div) => + { + let new = self.take_or_free_reg(I32).ok_or_else(|| error("Ran out of free registers"))?; + dynasm!(self.asm + ; mov Rq(new.rq().unwrap()), Rq(div_reg.rq().unwrap()) + ); + self.free(ValueLocation::Reg(div_reg))?; + div = ValueLocation::Reg(new); + }, + ValueLocation::Reg(_) | ValueLocation::Stack(_) | ValueLocation::Cond(_) | - ValueLocation::Immediate(_) => div, - }; + ValueLocation::Immediate(_) => {}, + } + + for (src, dst) in saved { + self.copy_value(src, dst)?; + self.free(src)?; + } - self.cleanup_gprs(saved); + debug_assert!(self.stack.iter().all(|v| { + if let ValueLocation::Stack(o) = v { + debug_assert_ge!(self.adjusted_offset(*o), 0); + } + + true + })); self.push(div)?; Ok(()) @@ -881,29 +1123,31 @@ macro_rules! int_div { return Ok(()) } - let (div, rem, saved) = self.$full_div_s(divisor, dividend)?; + let (mut div, rem, saved) = self.$full_div_s(divisor, dividend)?; - self.free_value(rem)?; + self.free(rem)?; - let div = match div { - ValueLocation::Reg(div) => { - if saved.clone().any(|dst| dst == div) { - let new = self.take_reg(I32).unwrap(); - dynasm!(self.asm - ; mov Rq(new.rq().unwrap()), Rq(div.rq().unwrap()) - ); - self.block_state.regs.release(div)?; - ValueLocation::Reg(new) - } else { - ValueLocation::Reg(div) - } - } + match div { + ValueLocation::Reg(div_reg) + if saved.clone().any(|(_, dst)| ValueLocation::from(dst) == div) => + { + let new = self.take_or_free_reg(I32).ok_or_else(|| error("Ran out of free registers"))?; + dynasm!(self.asm + ; mov Rq(new.rq().unwrap()), Rq(div_reg.rq().unwrap()) + ); + self.free(div)?; + div = ValueLocation::Reg(new); + }, + ValueLocation::Reg(_) | ValueLocation::Stack(_) | ValueLocation::Cond(_) | - ValueLocation::Immediate(_) => div, - }; + ValueLocation::Immediate(_) => {}, + } - self.cleanup_gprs(saved); + for (src, dst) in saved { + self.copy_value(src, dst)?; + self.free(src)?; + } self.push(div)?; Ok(()) @@ -925,29 +1169,31 @@ macro_rules! int_div { return Ok(()); } - let (div, rem, saved) = self.$full_div_u(divisor, dividend)?; + let (div, mut rem, saved) = self.$full_div_u(divisor, dividend)?; - self.free_value(div)?; + self.free(div)?; - let rem = match rem { - ValueLocation::Reg(rem) => { - if saved.clone().any(|dst| dst == rem) { - let new = self.take_reg(I32).unwrap(); - dynasm!(self.asm - ; mov Rq(new.rq().unwrap()), Rq(rem.rq().unwrap()) - ); - self.block_state.regs.release(rem)?; - ValueLocation::Reg(new) - } else { - ValueLocation::Reg(rem) - } - } + match rem { + ValueLocation::Reg(rem_reg) + if saved.clone().any(|(_, dst)| ValueLocation::from(dst) == rem) => + { + let new = self.take_or_free_reg(I32).ok_or_else(|| error("Ran out of free registers"))?; + dynasm!(self.asm + ; mov Rq(new.rq().unwrap()), Rq(rem_reg.rq().unwrap()) + ); + self.free(rem)?; + rem = ValueLocation::Reg(new); + }, + ValueLocation::Reg(_) | ValueLocation::Stack(_) | ValueLocation::Cond(_) | - ValueLocation::Immediate(_) => rem, - }; + ValueLocation::Immediate(_) => {}, + } - self.cleanup_gprs(saved); + for (src, dst) in saved { + self.copy_value(src, dst)?; + self.free(src)?; + } self.push(rem)?; Ok(()) @@ -969,21 +1215,21 @@ macro_rules! int_div { let is_neg1 = self.create_label(); - let current_depth = self.block_state.depth.clone(); + let current_depth = self.physical_stack_depth.clone(); // TODO: This could cause segfaults because of implicit push/pop let gen_neg1_case = match divisor { ValueLocation::Immediate(_) => { if divisor.$imm_fn().unwrap() == -1 { self.push(ValueLocation::Immediate((-1 as $signed_ty).into()))?; - self.free_value(dividend)?; + self.free(dividend)?; return Ok(()); } false } ValueLocation::Reg(_) => { - let reg = self.put_into_register(GPRType::Rq, &mut divisor)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let reg = self.put_into_register(GPRType::Rq, &mut divisor)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; cmp $reg_ty(reg.rq().unwrap()), -1 @@ -1015,29 +1261,31 @@ macro_rules! int_div { } }; - let (div, rem, saved) = self.$full_div_s(divisor, dividend)?; + let (div, mut rem, saved) = self.$full_div_s(divisor, dividend)?; - self.free_value(div)?; + self.free(div)?; - let rem = match rem { - ValueLocation::Reg(rem) => { - if saved.clone().any(|dst| dst == rem) { - let new = self.take_reg(I32).unwrap(); - dynasm!(self.asm - ; mov Rq(new.rq().unwrap()), Rq(rem.rq().unwrap()) - ); - self.block_state.regs.release(rem)?; - ValueLocation::Reg(new) - } else { - ValueLocation::Reg(rem) - } - } + match rem { + ValueLocation::Reg(rem_reg) + if saved.clone().any(|(_, dst)| ValueLocation::from(dst) == rem) => + { + let new = self.take_or_free_reg(I32).ok_or_else(|| error("Ran out of free registers"))?; + dynasm!(self.asm + ; mov Rq(new.rq().unwrap()), Rq(rem_reg.rq().unwrap()) + ); + self.free(rem)?; + rem = ValueLocation::Reg(new); + }, + ValueLocation::Reg(_) | ValueLocation::Stack(_) | ValueLocation::Cond(_) | - ValueLocation::Immediate(_) => rem, - }; + ValueLocation::Immediate(_) => {}, + } - self.cleanup_gprs(saved); + for (src, dst) in saved { + self.copy_value(src, dst)?; + self.free(src)?; + } if gen_neg1_case { let ret = self.create_label(); @@ -1047,10 +1295,10 @@ macro_rules! int_div { ); self.define_label(is_neg1); - let dst_ccloc = match CCLoc::try_from(rem) { + let dst_ccloc = match CCLoc::try_from(rem).ok() { None => { - return Err(Error::Microwasm( - "$rem_s Programmer error".to_string(), + return Err(error( + "$rem_s Programmer error".to_string() )) } Some(o) => o, @@ -1083,15 +1331,15 @@ macro_rules! unop { ), ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); - let temp = self.take_reg(Type::for_::<$typ>()).unwrap(); + let temp = self.take_or_free_reg(Type::for_::<$typ>()).unwrap(); dynasm!(self.asm ; $instr $reg_ty(temp.rq().unwrap()), [rsp + offset] ); ValueLocation::Reg(temp) } ValueLocation::Reg(_) | ValueLocation::Cond(_) => { - let reg = self.put_into_register(GPRType::Rq, &mut val)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; - let temp = self.take_reg(Type::for_::<$typ>()).unwrap(); + let reg = self.put_into_register(GPRType::Rq, &mut val)?.ok_or_else(|| error("Ran out of free registers"))?; + let temp = self.take_or_free_reg(Type::for_::<$typ>()).unwrap(); dynasm!(self.asm ; $instr $reg_ty(temp.rq().unwrap()), $reg_ty(reg.rq().unwrap()) ); @@ -1099,7 +1347,7 @@ macro_rules! unop { } }; - self.free_value(val)?; + self.free(val)?; self.push(out_val)?; Ok(()) } @@ -1129,7 +1377,7 @@ macro_rules! conversion { ), ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); - let temp = self.take_reg(Type::for_::<$out_typ>()).unwrap(); + let temp = self.take_or_free_reg(Type::for_::<$out_typ>()).unwrap(); dynasm!(self.asm ; $instr $out_reg_ty(temp.$out_reg_fn().unwrap()), [rsp + offset] ); @@ -1137,8 +1385,8 @@ macro_rules! conversion { ValueLocation::Reg(temp) } ValueLocation::Reg(_) | ValueLocation::Cond(_) => { - let reg = self.put_into_register(Type::for_::<$in_typ>(), &mut val)? .ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; - let temp = self.take_reg(Type::for_::<$out_typ>()).unwrap(); + let reg = self.put_into_register(Type::for_::<$in_typ>(), &mut val)? .ok_or_else(|| error("Ran out of free registers"))?; + let temp = self.take_or_free_reg(Type::for_::<$out_typ>()).unwrap(); dynasm!(self.asm ; $instr $out_reg_ty(temp.$out_reg_fn().unwrap()), $in_reg_ty(reg.$in_reg_fn().unwrap()) @@ -1148,7 +1396,7 @@ macro_rules! conversion { } }; - self.free_value(val)?; + self.free(val)?; self.push(out_val)?; Ok(()) @@ -1166,7 +1414,7 @@ macro_rules! shift { if let Some(imm) = count.immediate() { if let Some(imm) = imm.as_int() { if let Ok(imm) = i8::try_from(imm) { - let reg = self.put_into_temp_register($ty, &mut val)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let reg = self.put_into_temp_register($ty, &mut val)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; $instr $reg_ty(reg.rq().unwrap()), imm @@ -1178,9 +1426,9 @@ macro_rules! shift { } if val == ValueLocation::Reg(RCX) { - let new = self.take_reg($ty).unwrap(); + let new = self.take_or_free_reg($ty).ok_or_else(|| error("Ran out of free registers"))?; self.copy_value(val, CCLoc::Reg(new))?; - self.free_value(val)?; + self.free(val)?; val = ValueLocation::Reg(new); } @@ -1190,57 +1438,39 @@ macro_rules! shift { let temp_rcx = match count { ValueLocation::Reg(RCX) => {None} other => { - let out = if self.block_state.regs.is_free(RCX) { + let out = if self.is_free(CCLoc::Reg(RCX))? { None } else { - let new_reg = self.take_reg(I32).unwrap(); + let new_reg = self.take_or_free_reg(I32).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; mov Rq(new_reg.rq().unwrap()), rcx ); Some(new_reg) }; - match other { - ValueLocation::Reg(_) | ValueLocation::Cond(_) => { - let gpr = self.put_into_register(I32, &mut count)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; - dynasm!(self.asm - ; mov cl, Rb(gpr.rq().unwrap()) - ); - } - ValueLocation::Stack(offset) => { - let offset = self.adjusted_offset(offset); - dynasm!(self.asm - ; mov cl, [rsp + offset] - ); - } - ValueLocation::Immediate(imm) => { - dynasm!(self.asm - ; mov cl, imm.as_int().unwrap() as i8 - ); - } - } + self.copy_value(other, CCLoc::Reg(RCX))?; out } }; - self.free_value(count)?; - self.block_state.regs.mark_used(RCX); + self.free(count)?; + self.mark_used(CCLoc::Reg(RCX))?; count = ValueLocation::Reg(RCX); - let reg = self.put_into_temp_register($ty, &mut val)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let reg = self.put_into_temp_register($ty, &mut val)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; $instr $reg_ty(reg.rq().unwrap()), cl ); - self.free_value(count)?; + self.free(count)?; if let Some(gpr) = temp_rcx { dynasm!(self.asm ; mov rcx, Rq(gpr.rq().unwrap()) ); - self.block_state.regs.release(gpr)?; + self.free(ValueLocation::Reg(gpr))?; } self.push(val)?; @@ -1266,7 +1496,7 @@ macro_rules! cmp_i32 { ValueLocation::Cond($reverse_flags) } ValueLocation::Reg(_) | ValueLocation::Cond(_) => { - let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; cmp Rd(rreg.rq().unwrap()), i ); @@ -1283,7 +1513,7 @@ macro_rules! cmp_i32 { } } } else { - let lreg = self.put_into_register(I32, &mut left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = self.put_into_register(I32, &mut left)?.ok_or_else(|| error("Ran out of free registers"))?; match right { ValueLocation::Stack(offset) => { @@ -1293,7 +1523,7 @@ macro_rules! cmp_i32 { ); } ValueLocation::Reg(_) | ValueLocation::Cond(_) => { - let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; cmp Rd(lreg.rq().unwrap()), Rd(rreg.rq().unwrap()) @@ -1309,8 +1539,8 @@ macro_rules! cmp_i32 { ValueLocation::Cond($flags) }; - self.free_value(left)?; - self.free_value(right)?; + self.free(left)?; + self.free(right)?; self.push(out)?; Ok(()) @@ -1333,7 +1563,7 @@ macro_rules! cmp_i64 { ; cmp QWORD [rsp + offset], i ); } else { - let lreg = self.put_into_register(I32, &mut left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = self.put_into_register(I32, &mut left)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; cmp QWORD [rsp + offset], Rq(lreg.rq().unwrap()) @@ -1342,14 +1572,14 @@ macro_rules! cmp_i64 { ValueLocation::Cond($reverse_flags) } ValueLocation::Reg(_) | ValueLocation::Cond(_) => { - let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; if let Some(i) = i.try_into().ok() { dynasm!(self.asm ; cmp Rq(rreg.rq().unwrap()), i ); } else { - let lreg = self.put_into_register(I32, &mut left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = self.put_into_register(I32, &mut left)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; cmp Rq(rreg.rq().unwrap()), Rq(lreg.rq().unwrap()) @@ -1368,7 +1598,7 @@ macro_rules! cmp_i64 { } } } else { - let lreg = self.put_into_register(I64, &mut left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = self.put_into_register(I64, &mut left)?.ok_or_else(|| error("Ran out of free registers"))?; match right { ValueLocation::Stack(offset) => { @@ -1378,7 +1608,7 @@ macro_rules! cmp_i64 { ); } ValueLocation::Reg(_) | ValueLocation::Cond(_) => { - let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; cmp Rq(lreg.rq().unwrap()), Rq(rreg.rq().unwrap()) @@ -1388,10 +1618,10 @@ macro_rules! cmp_i64 { let i = i.as_i64().unwrap(); if let Some(i) = i.try_into().ok() { dynasm!(self.asm - ; cmp Rq(lreg.rq().unwrap()), i + ; cmp Rq(lreg.rq().unwrap()), i ); } else { - let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let rreg = self.put_into_register(I32, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; cmp Rq(lreg.rq().unwrap()), Rq(rreg.rq().unwrap()) @@ -1403,8 +1633,8 @@ macro_rules! cmp_i64 { ValueLocation::Cond($flags) }; - self.free_value(left)?; - self.free_value(right)?; + self.free(left)?; + self.free(right)?; self.push(out)?; Ok(()) } @@ -1445,14 +1675,14 @@ macro_rules! eq_float { } let (mut left, mut right) = match left { - ValueLocation::Reg(r) if self.block_state.regs.num_usages(r) <= 1 => (left, right), + ValueLocation::Reg(r) if self.num_usages(CCLoc::Reg(r))? <= 1 => (left, right), _ => (right, left) }; - let lreg = self.put_into_temp_register(GPRType::Rx, &mut left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; - let rreg = self.put_into_register(GPRType::Rx, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = self.put_into_temp_register(GPRType::Rx, &mut left)?.ok_or_else(|| error("Ran out of free registers"))?; + let rreg = self.put_into_register(GPRType::Rx, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; - let out = self.take_reg(I32).unwrap(); + let out = self.take_or_free_reg(I32).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; $instr Rx(lreg.rx().unwrap()), Rx(rreg.rx().unwrap()) @@ -1461,8 +1691,8 @@ macro_rules! eq_float { ); self.push(ValueLocation::Reg(out))?; - self.free_value(left)?; - self.free_value(right)?; + self.free(left)?; + self.free(right)?; Ok(()) } @@ -1493,12 +1723,12 @@ macro_rules! minmax_float { } let (mut left, mut right) = match left { - ValueLocation::Reg(r) if self.block_state.regs.num_usages(r) <= 1 => (left, right), + ValueLocation::Reg(r) if self.num_usages(CCLoc::Reg(r))? <= 1 => (left, right), _ => (right, left) }; - let lreg = self.put_into_temp_register(GPRType::Rx, &mut left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; - let rreg = self.put_into_register(GPRType::Rx, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = self.put_into_temp_register(GPRType::Rx, &mut left)?.ok_or_else(|| error("Ran out of free registers"))?; + let rreg = self.put_into_register(GPRType::Rx, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; $cmpinstr Rx(lreg.rx().unwrap()), Rx(rreg.rx().unwrap()) @@ -1515,7 +1745,7 @@ macro_rules! minmax_float { ); self.push(left)?; - self.free_value(right)?; + self.free(right)?; Ok(()) } @@ -1546,9 +1776,9 @@ macro_rules! cmp_float { ValueLocation::Immediate(0i32.into()) } } else { - let lreg = this.put_into_register(GPRType::Rx, left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = this.put_into_register(GPRType::Rx, left)?.ok_or_else(|| error("Ran out of free registers"))?; - let result = this.take_reg(I32).unwrap(); + let result = this.take_or_free_reg(I32).ok_or_else(|| error("Ran out of free registers"))?; match right { ValueLocation::Stack(offset) => { @@ -1561,7 +1791,7 @@ macro_rules! cmp_float { ); } right => { - let rreg = this.put_into_register(GPRType::Rx, right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let rreg = this.put_into_register(GPRType::Rx, right)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(this.asm ; xor Rq(result.rq().unwrap()), Rq(result.rq().unwrap()) @@ -1590,8 +1820,8 @@ macro_rules! cmp_float { $const_fallback ); - self.free_value(left)?; - self.free_value(right)?; + self.free(left)?; + self.free(right)?; self.push(out)?; Ok(()) @@ -1612,8 +1842,8 @@ macro_rules! cmp_float { $const_fallback ); - self.free_value(left)?; - self.free_value(right)?; + self.free(left)?; + self.free(right)?; self.push(out)?; Ok(()) @@ -1792,18 +2022,18 @@ macro_rules! binop { if let Some(i1) = left.$imm_fn() { if let Some(i0) = right.$imm_fn() { - self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into())); + self.push(ValueLocation::Immediate($const_fallback(i1, i0).into()))?; return Ok(()); } } let (mut left, mut right) = $map_op(left, right); - let lreg = self.put_into_temp_register($ty, &mut left)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let lreg = self.put_into_temp_register($ty, &mut left)?.ok_or_else(|| error("Ran out of free registers"))?; match right { ValueLocation::Reg(_) | ValueLocation::Cond(_) => { // This handles the case where we (for example) have a float in an `Rq` reg - let right_reg = self.put_into_register($ty, &mut right)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let right_reg = self.put_into_register($ty, &mut right)?.ok_or_else(|| error("Ran out of free registers"))?; dynasm!(self.asm ; $instr $reg_ty(lreg.$reg_fn().unwrap()), $reg_ty(right_reg.$reg_fn().unwrap()) @@ -1819,19 +2049,19 @@ macro_rules! binop { if let Some(i) = i.as_int().and_then(|i| i.try_into().ok()) { $direct_imm(&mut *self, lreg, i); } else { - let scratch = self.take_reg($ty).unwrap(); + let scratch = self.take_or_free_reg($ty).ok_or_else(|| error("Ran out of free registers"))?; self.immediate_to_reg(scratch, i)?; dynasm!(self.asm ; $instr $reg_ty(lreg.$reg_fn().unwrap()), $reg_ty(scratch.$reg_fn().unwrap()) ); - self.block_state.regs.release(scratch)?; + self.free(ValueLocation::Reg(scratch))?; } } } - self.free_value(right)?; + self.free(right)?; self.push(left)?; Ok(()) } @@ -1853,8 +2083,10 @@ macro_rules! load { None, ctx.module_context.vmctx_vmmemory_definition(index) as i32 )); - let (reg, mem_offset) = reg_offset.unwrap_or_else(|| { - let reg = ctx.take_reg(I64).unwrap(); + let (reg, mem_offset) = reg_offset.ok_or(()).or_else::(|_| { + let reg = ctx + .take_or_free_reg(I64) + .ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rq(reg.rq().unwrap()), [ @@ -1862,15 +2094,15 @@ macro_rules! load { ] ); - (Some(reg), 0) - }); + Ok((Some(reg), 0)) + })?; let vmctx = GPR::Rq(VMCTX); if ctx.module_context.emit_memory_bounds_check() { let addr_reg = match runtime_offset { Ok(imm) => { - let addr_reg = ctx.take_reg(I64).unwrap(); + let addr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rq(addr_reg.rq().unwrap()), QWORD imm as i64 + offset as i64 ); @@ -1878,22 +2110,22 @@ macro_rules! load { } Err(gpr) => { if offset == 0 { - ctx.clone_to_register(I32, ValueLocation::Reg(gpr))?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))? + ctx.clone_to_register(I32, ValueLocation::Reg(gpr))?.ok_or_else(|| error("Ran out of free registers"))? } else if offset > 0 { - let addr_reg = ctx.take_reg(I64).unwrap(); + let addr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; lea Rq(addr_reg.rq().unwrap()), [Rq(gpr.rq().unwrap()) + offset] ); addr_reg } else { - let addr_reg = ctx.take_reg(I64).unwrap(); - let offset_reg = ctx.take_reg(I64).unwrap(); + let addr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; + let offset_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rd(offset_reg.rq().unwrap()), offset ; mov Rq(addr_reg.rq().unwrap()), Rq(gpr.rq().unwrap()) ; add Rq(addr_reg.rq().unwrap()), Rq(offset_reg.rq().unwrap()) ); - ctx.block_state.regs.release(offset_reg)?; + ctx.free(ValueLocation::Reg(offset_reg))?; addr_reg } } @@ -1906,10 +2138,10 @@ macro_rules! load { ] ;; ctx.trap_if(cc::GE_U, TrapCode::HeapOutOfBounds) ); - ctx.block_state.regs.release(addr_reg)?; + ctx.free(ValueLocation::Reg(addr_reg))?; } - let mem_ptr_reg = ctx.take_reg(I64).unwrap(); + let mem_ptr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rq(mem_ptr_reg.rq().unwrap()), [ Rq(reg.unwrap_or(vmctx).rq().unwrap()) + @@ -1918,25 +2150,26 @@ macro_rules! load { ] ); if let Some(reg) = reg { - ctx.block_state.regs.release(reg)?; + ctx.free(ValueLocation::Reg(reg))?; } $emit_fn(ctx, dst, mem_ptr_reg, runtime_offset, offset)?; - ctx.block_state.regs.release(mem_ptr_reg)?; + ctx.free(ValueLocation::Reg(mem_ptr_reg))?; Ok(()) } let base = self.pop()?; - let temp = self.take_reg($rtype).unwrap(); + let temp = self.take_or_free_reg($rtype) + .ok_or_else(|| error("Ran out of free registers"))?; match base { ValueLocation::Immediate(i) => { load_to_reg(self, temp, (offset as _, Ok(i.as_i32().unwrap())))?; } mut base => { - let gpr = self.put_into_register(I32, &mut base)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let gpr = self.put_into_register(I32, &mut base)?.ok_or_else(|| error("Ran out of free registers"))?; load_to_reg(self, temp, (offset as _, Err(gpr)))?; - self.free_value(base)?; + self.free(base)?; } } @@ -1987,7 +2220,7 @@ macro_rules! load { ); Ok(()) } else { - let offset_reg = ctx.take_reg(GPRType::Rq).unwrap(); + let offset_reg = ctx.take_or_free_reg(GPRType::Rq).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rq(offset_reg.rq().unwrap()), offset ; $xmm_instr Rx(r), $ty [ @@ -1996,7 +2229,7 @@ macro_rules! load { imm ] ); - ctx.block_state.regs.release(offset_reg)?; + ctx.free(ValueLocation::Reg(offset_reg))?; Ok(()) } } @@ -2033,8 +2266,10 @@ macro_rules! store { None, ctx.module_context.vmctx_vmmemory_definition(index) as i32 )); - let (reg, mem_offset) = reg_offset.unwrap_or_else(|| { - let reg = ctx.take_reg(I64).unwrap(); + let (reg, mem_offset) = reg_offset.ok_or(()).or_else::(|_| { + let reg = ctx + .take_or_free_reg(I64) + .ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rq(reg.rq().unwrap()), [ @@ -2042,15 +2277,15 @@ macro_rules! store { ] ); - (Some(reg), 0) - }); + Ok((Some(reg), 0)) + })?; let vmctx = GPR::Rq(VMCTX); if ctx.module_context.emit_memory_bounds_check() { let addr_reg = match runtime_offset { Ok(imm) => { - let addr_reg = ctx.take_reg(I64).unwrap(); + let addr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rq(addr_reg.rq().unwrap()), QWORD imm as i64 + offset as i64 ); @@ -2058,23 +2293,23 @@ macro_rules! store { } Err(gpr) => { if offset == 0 { - ctx.clone_to_register(I32, ValueLocation::Reg(gpr))?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))? + ctx.clone_to_register(I32, ValueLocation::Reg(gpr))?.ok_or_else(|| error("Ran out of free registers"))? } else if offset > 0 { - let addr_reg = ctx.take_reg(I64).unwrap(); + let addr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; lea Rq(addr_reg.rq().unwrap()), [Rq(gpr.rq().unwrap()) + offset] ); addr_reg } else { - let addr_reg = ctx.take_reg(I64).unwrap(); - let offset_reg = ctx.take_reg(I64).unwrap(); + let addr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; + let offset_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rd(offset_reg.rq().unwrap()), offset ; mov Rq(addr_reg.rq().unwrap()), Rq(gpr.rq().unwrap()) ; add Rq(addr_reg.rq().unwrap()), Rq(offset_reg.rq().unwrap()) ); - ctx.block_state.regs.release(offset_reg)?; + ctx.free(ValueLocation::Reg(offset_reg))?; addr_reg } } @@ -2087,10 +2322,10 @@ macro_rules! store { ] ;; ctx.trap_if(cc::GE_U, TrapCode::HeapOutOfBounds) ); - ctx.block_state.regs.release(addr_reg)?; + ctx.free(ValueLocation::Reg(addr_reg))?; } - let mem_ptr_reg = ctx.take_reg(I64).unwrap(); + let mem_ptr_reg = ctx.take_or_free_reg(I64).ok_or_else(|| error("Ran out of free registers"))?; dynasm!(ctx.asm ; mov Rq(mem_ptr_reg.rq().unwrap()), [ Rq(reg.unwrap_or(vmctx).rq().unwrap()) + @@ -2099,16 +2334,16 @@ macro_rules! store { ] ); if let Some(reg) = reg { - ctx.block_state.regs.release(reg)?; + ctx.free(ValueLocation::Reg(reg))?; } let src = $match_offset(ctx, mem_ptr_reg, runtime_offset, offset, src)?; - ctx.block_state.regs.release(mem_ptr_reg)?; - ctx.block_state.regs.release(src)?; + ctx.free(ValueLocation::Reg(mem_ptr_reg))?; + ctx.free(ValueLocation::Reg(src))?; Ok(()) } if !(offset <= i32::max_value() as u32) { - return Err(Error::Microwasm(format!("store: offset value too big {}", offset))) + return Err(error(format!("store: offset value too big {}", offset))) } let mut src = self.pop()?; @@ -2116,7 +2351,7 @@ macro_rules! store { // `store_from_reg` frees `src` // TODO: Would it be better to free it outside `store_from_reg`? - let src_reg = self.put_into_register(None, &mut src)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let src_reg = self.put_into_register(None, &mut src)?.ok_or_else(|| error("Ran out of free registers"))?; match base { @@ -2124,9 +2359,9 @@ macro_rules! store { store_from_reg(self, src_reg, (offset as i32, Ok(i.as_i32().unwrap())))? } mut base => { - let gpr = self.put_into_register(I32, &mut base)?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let gpr = self.put_into_register(I32, &mut base)?.ok_or_else(|| error("Ran out of free registers"))?; store_from_reg(self, src_reg, (offset as i32, Err(gpr)))?; - self.free_value(base)?; + self.free(base)?; } } Ok(()) @@ -2137,7 +2372,7 @@ macro_rules! store { $name, $int_reg_ty, |ctx: &mut Context<_>, mem_ptr_reg: GPR, runtime_offset: Result, offset: i32, src| -> Result { - let src_reg = ctx.put_into_temp_register(GPRType::Rq, &mut ValueLocation::Reg(src))?.ok_or_else(|| Error::Microwasm("Ran out of free registers".to_string()))?; + let src_reg = ctx.put_into_temp_register(GPRType::Rq, &mut ValueLocation::Reg(src))?.ok_or_else(|| error("Ran out of free registers"))?; match runtime_offset { Ok(imm) => { @@ -2192,16 +2427,80 @@ macro_rules! store { }; } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VirtualCallingConvention { - pub stack: Stack, - pub depth: StackDepth, +macro_rules! sign_extend { + ($fn_name:ident, ($outty:ident, $outreg:tt), ($inty:ident, $inreg:tt), $size:ident) => { + pub fn $fn_name(&mut self) -> Result<(), Error> { + let val = self.pop()?; + + let out = if let ValueLocation::Immediate(imm) = val { + ValueLocation::Immediate((imm.as_bytes() as $inty as $outty).into()) + } else { + let new_reg = self + .take_or_free_reg(I64) + .ok_or_else(|| error("Ran out of free registers"))?; + + // TODO: Track set-ness of bits - we can make this a no-op in most cases + // but we have to make this unconditional just in case this value + // came from a truncate. + match val { + ValueLocation::Reg(GPR::Rx(rxreg)) => { + dynasm!(self.asm + ; movd Rd(new_reg.rq().unwrap()), Rx(rxreg) + ); + + self.free(val)?; + self.push(ValueLocation::Reg(new_reg))?; + + return self.$fn_name(); + } + ValueLocation::Reg(GPR::Rq(rqreg)) => { + dynasm!(self.asm + ; movsx $outreg(new_reg.rq().unwrap()), $inreg(rqreg) + ); + } + ValueLocation::Stack(offset) => { + let offset = self.adjusted_offset(offset); + + dynasm!(self.asm + ; movsx $outreg(new_reg.rq().unwrap()), $size [rsp + offset] + ); + } + ValueLocation::Cond(_) => self.copy_value(val, CCLoc::Reg(new_reg))?, + ValueLocation::Immediate(_) => return Err(error("unreachable")), + } + + ValueLocation::Reg(new_reg) + }; + + self.free(val)?; + + self.push(out)?; + Ok(()) + } + } +} + +#[derive(PartialEq, Eq, Copy, Clone)] +pub enum BrAction { + Jump, + Continue, +} + +pub struct Target { + pub target: BrTarget