diff --git a/.gitignore b/.gitignore index 4c37c4f83d..f890733b7d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ docs/book rusty-tags.* tags target +.z3-trace diff --git a/Cargo.lock b/Cargo.lock index c143251096..99373b1fae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,9 +11,12 @@ dependencies = [ [[package]] name = "ahash" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" +checksum = "0989268a37e128d4d7a8028f1c60099430113fdbc70419010601ce51a228e4fe" +dependencies = [ + "const-random", +] [[package]] name = "aho-corasick" @@ -41,9 +44,9 @@ checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" [[package]] name = "arbitrary" -version = "0.4.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5eb01a9ab8a3369f2f7632b9461c34f5920bd454774bab5b9fc6744f21d6143" +checksum = "75153c95fdedd7db9732dfbfc3702324a1627eec91ba56e37cd0ac78314ab2ed" dependencies = [ "derive_arbitrary", ] @@ -97,9 +100,9 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.36" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7" +checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" dependencies = [ "cc", "libc", @@ -201,6 +204,12 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "bumpalo" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" + [[package]] name = "byte-tools" version = "0.3.1" @@ -233,9 +242,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.52" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" dependencies = [ "jobserver", ] @@ -261,6 +270,18 @@ dependencies = [ "vec_map", ] +[[package]] +name = "clicolors-control" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e" +dependencies = [ + "atty", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -281,19 +302,18 @@ dependencies = [ [[package]] name = "console" -version = "0.11.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0f3e2e8d7dba335e913b97f9e1992c86c4399d54f8be1d31c8727d0652064" +checksum = "6728a28023f207181b193262711102bfbaf47cc9d13bc71d0736607ef8efe88c" dependencies = [ + "clicolors-control", "encode_unicode", "lazy_static", "libc", "regex", - "terminal_size", "termios", "unicode-width", "winapi", - "winapi-util", ] [[package]] @@ -357,8 +377,10 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "gimli", - "hashbrown 0.7.2", + "hashbrown 0.7.1", "log", + "peepmatic", + "peepmatic-runtime", "regalloc", "serde", "smallvec", @@ -424,7 +446,7 @@ name = "cranelift-frontend" version = "0.63.0" dependencies = [ "cranelift-codegen", - "hashbrown 0.7.2", + "hashbrown 0.7.1", "log", "smallvec", "target-lexicon", @@ -438,7 +460,7 @@ dependencies = [ "cranelift-entity", "cranelift-frontend", "cranelift-reader", - "hashbrown 0.7.2", + "hashbrown 0.7.1", "log", "pretty_env_logger", "thiserror", @@ -566,12 +588,12 @@ dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "hashbrown 0.7.2", + "hashbrown 0.7.1", "log", "serde", "target-lexicon", "thiserror", - "wasmparser", + "wasmparser 0.52.2", "wat", ] @@ -642,13 +664,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "0.4.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee758ebd1c79a9c6fb95f242dcc30bdbf555c28369ae908d21fdaf81537496" +checksum = "caedd6a71b6d00bdc458ec8ffbfd12689c1ee7ffa69ad9933310aaf2f08f18d8" dependencies = [ "proc-macro2", - "quote", "syn", + "synstructure", ] [[package]] @@ -877,6 +899,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +[[package]] +name = "fst" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f9cac32c1741cdf6b66be7dcf0d9c7f25ccf12f8aa84c16cfa31f9f14513b3" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -952,11 +980,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +checksum = "479e9d9a1a3f8c489868a935b557ab5710e3e223836da2ecd52901d88935cb56" dependencies = [ - "ahash 0.3.3", + "ahash 0.3.2", "autocfg 1.0.0", ] @@ -971,9 +999,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.12" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" dependencies = [ "libc", ] @@ -1100,7 +1128,7 @@ dependencies = [ "staticvec", "thiserror", "typemap", - "wasmparser", + "wasmparser 0.52.2", "wat", ] @@ -1170,9 +1198,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" dependencies = [ "hermit-abi", "libc", @@ -1226,6 +1254,76 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "peepmatic" +version = "0.1.0" +dependencies = [ + "anyhow", + "peepmatic-automata", + "peepmatic-macro", + "peepmatic-runtime", + "wast 13.0.0", + "z3", +] + +[[package]] +name = "peepmatic-automata" +version = "0.1.0" +dependencies = [ + "serde", +] + +[[package]] +name = "peepmatic-fuzzing" +version = "0.1.0" +dependencies = [ + "arbitrary", + "bincode", + "env_logger 0.7.1", + "fst", + "log", + "peepmatic", + "peepmatic-automata", + "peepmatic-runtime", + "peepmatic-test", + "rand 0.7.3", + "serde", + "wast 13.0.0", +] + +[[package]] +name = "peepmatic-macro" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "peepmatic-runtime" +version = "0.1.0" +dependencies = [ + "bincode", + "bumpalo", + "log", + "peepmatic-automata", + "peepmatic-macro", + "serde", + "thiserror", + "wast 13.0.0", +] + +[[package]] +name = "peepmatic-test" +version = "0.1.0" +dependencies = [ + "env_logger 0.7.1", + "log", + "peepmatic", + "peepmatic-runtime", +] + [[package]] name = "plain" version = "0.2.3" @@ -1291,9 +1389,9 @@ dependencies = [ [[package]] name = "proptest" -version = "0.9.6" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c477819b845fe023d33583ebf10c9f62518c8d79a0960ba5c36d6ac8a55a5b" +checksum = "bf6147d103a7c9d7598f4105cf049b15c99e2ecd93179bf024f0fd349be5ada4" dependencies = [ "bit-set", "bitflags", @@ -1350,7 +1448,7 @@ dependencies = [ "rand_isaac", "rand_jitter", "rand_os", - "rand_pcg", + "rand_pcg 0.1.2", "rand_xorshift", "winapi", ] @@ -1366,6 +1464,7 @@ dependencies = [ "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc 0.2.0", + "rand_pcg 0.2.1", ] [[package]] @@ -1474,6 +1573,15 @@ dependencies = [ "rand_core 0.4.2", ] +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + [[package]] name = "rand_xorshift" version = "0.1.1" @@ -1557,9 +1665,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.7" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" dependencies = [ "aho-corasick", "memchr", @@ -1648,9 +1756,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" [[package]] name = "same-file" @@ -1724,9 +1832,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.52" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" +checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" dependencies = [ "itoa", "ryu", @@ -1747,9 +1855,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.4.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" [[package]] name = "stable_deref_trait" @@ -1780,9 +1888,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.14" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" +checksum = "ff6da2e8d107dfd7b74df5ef4d205c6aebee0706c647f6bc6a2d5789905c00fb" dependencies = [ "clap", "lazy_static", @@ -1791,9 +1899,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" +checksum = "a489c87c08fbaf12e386665109dd13470dcc9c4583ea3e10dd2b4523e5ebd9ac" dependencies = [ "heck", "proc-macro-error", @@ -1804,9 +1912,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ "proc-macro2", "quote", @@ -1824,6 +1932,18 @@ dependencies = [ "syn", ] +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "target-lexicon" version = "0.10.0" @@ -1863,16 +1983,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8038f95fc7a6f351163f4b964af631bd26c9e828f7db085f2a84aca56f70d13b" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "termios" version = "0.3.2" @@ -1909,18 +2019,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.16" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60" +checksum = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.16" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269" +checksum = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd" dependencies = [ "proc-macro2", "quote", @@ -1962,9 +2072,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.12.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" [[package]] name = "unicode-segmentation" @@ -2051,6 +2161,12 @@ dependencies = [ "yanix", ] +[[package]] +name = "wasmparser" +version = "0.51.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a" + [[package]] name = "wasmparser" version = "0.52.2" @@ -2059,12 +2175,12 @@ checksum = "733954023c0b39602439e60a65126fd31b003196d3a1e8e4531b055165a79b31" [[package]] name = "wasmprinter" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21c3ac16b1f882bf1e1ce007e4f194559d2e3332013367863ddfbc828d1f044f" +checksum = "8bd423d45b95fcee11775472bfdce66c63c45ada23c1b338e0a63d623a6c475b" dependencies = [ "anyhow", - "wasmparser", + "wasmparser 0.51.4", ] [[package]] @@ -2080,7 +2196,7 @@ dependencies = [ "rustc-demangle", "target-lexicon", "tempfile", - "wasmparser", + "wasmparser 0.52.2", "wasmtime-environ", "wasmtime-jit", "wasmtime-profiling", @@ -2150,7 +2266,7 @@ dependencies = [ "more-asserts", "target-lexicon", "thiserror", - "wasmparser", + "wasmparser 0.52.2", "wasmtime-environ", ] @@ -2181,7 +2297,7 @@ dependencies = [ "tempfile", "thiserror", "toml", - "wasmparser", + "wasmparser 0.52.2", "winapi", "zstd", ] @@ -2194,6 +2310,7 @@ dependencies = [ "cranelift-reader", "cranelift-wasm", "libfuzzer-sys", + "peepmatic-fuzzing", "target-lexicon", "wasmtime", "wasmtime-fuzzing", @@ -2209,7 +2326,7 @@ dependencies = [ "env_logger 0.7.1", "log", "rayon", - "wasmparser", + "wasmparser 0.52.2", "wasmprinter", "wasmtime", "wasmtime-wast", @@ -2233,7 +2350,7 @@ dependencies = [ "region", "target-lexicon", "thiserror", - "wasmparser", + "wasmparser 0.52.2", "wasmtime-debug", "wasmtime-environ", "wasmtime-profiling", @@ -2336,6 +2453,15 @@ dependencies = [ "leb128", ] +[[package]] +name = "wast" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b20abd8b4a26f7e0d4dd5e357e90a3d555ec190e94472c9b2b27c5b9777f9ae" +dependencies = [ + "leb128", +] + [[package]] name = "wast" version = "15.0.0" @@ -2347,11 +2473,11 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.16" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526d28df6c047d9f9a92d4925b98afd8d8d95b1b3aa4f13eb1306f17d1da56c4" +checksum = "51a615830ee3e7200b505c441fec09aac2f114deae69df52f215cb828ba112c4" dependencies = [ - "wast 15.0.0", + "wast 13.0.0", ] [[package]] @@ -2424,9 +2550,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" dependencies = [ "winapi", ] @@ -2470,6 +2596,26 @@ dependencies = [ "log", ] +[[package]] +name = "z3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd636f549e919f8058567000156e19efbb4728ddb3be226dcc332d0ff622ab5" +dependencies = [ + "lazy_static", + "log", + "z3-sys", +] + +[[package]] +name = "z3-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2487974559d1494e8e8925df85362d6109ae5555092510508a5b1077346c2833" +dependencies = [ + "cmake", +] + [[package]] name = "zstd" version = "0.5.1+zstd.1.4.4" diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index 692c85bdd4..4737904eb3 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -24,6 +24,7 @@ gimli = { version = "0.20.0", default-features = false, features = ["write"], op smallvec = { version = "1.0.0" } thiserror = "1.0.4" byteorder = { version = "1.3.2", default-features = false } +peepmatic-runtime = { path = "../peepmatic/crates/runtime" } regalloc = "0.0.23" # It is a goal of the cranelift-codegen crate to have minimal external dependencies. # Please don't add any unless they are essential to the task of creating binary @@ -32,6 +33,7 @@ regalloc = "0.0.23" [build-dependencies] cranelift-codegen-meta = { path = "meta", version = "0.63.0" } +peepmatic = { path = "../peepmatic", optional = true } [features] default = ["std", "unwind"] @@ -72,5 +74,9 @@ all-arch = [ # For dependent crates that want to serialize some parts of cranelift enable-serde = ["serde"] +# Recompile our optimizations that are written in the peepmatic DSL into a +# compact finite-state transducer automaton. +rebuild-peephole-optimizers = ["peepmatic"] + [badges] maintenance = { status = "experimental" } diff --git a/cranelift/codegen/build.rs b/cranelift/codegen/build.rs index bb14364050..b7352f37c3 100644 --- a/cranelift/codegen/build.rs +++ b/cranelift/codegen/build.rs @@ -71,4 +71,22 @@ fn main() { ); println!("cargo:warning=Generated files are in {}", out_dir); } + + #[cfg(feature = "rebuild-peephole-optimizers")] + rebuild_peephole_optimizers(); +} + +#[cfg(feature = "rebuild-peephole-optimizers")] +fn rebuild_peephole_optimizers() { + use std::path::Path; + + let source_path = Path::new("src").join("preopt.peepmatic"); + println!("cargo:rerun-if-changed={}", source_path.display()); + + let preopt = + peepmatic::compile_file(&source_path).expect("failed to compile `src/preopt.peepmatic`"); + + preopt + .serialize_to_file(&Path::new("src").join("preopt.serialized")) + .expect("failed to serialize peephole optimizer to `src/preopt.serialized`"); } diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs index 787f519de9..58d101aace 100644 --- a/cranelift/codegen/src/ir/dfg.rs +++ b/cranelift/codegen/src/ir/dfg.rs @@ -234,11 +234,7 @@ impl DataFlowGraph { /// Get the type of a value. pub fn value_type(&self, v: Value) -> Type { - match self.values[v] { - ValueData::Inst { ty, .. } - | ValueData::Param { ty, .. } - | ValueData::Alias { ty, .. } => ty, - } + self.values[v].ty() } /// Get the definition of a value. @@ -383,9 +379,14 @@ pub enum ValueDef { impl ValueDef { /// Unwrap the instruction where the value was defined, or panic. pub fn unwrap_inst(&self) -> Inst { + self.inst().expect("Value is not an instruction result") + } + + /// Get the instruction where the value was defined, if any. + pub fn inst(&self) -> Option { match *self { - Self::Result(inst, _) => inst, - _ => panic!("Value is not an instruction result"), + Self::Result(inst, _) => Some(inst), + _ => None, } } @@ -428,6 +429,16 @@ enum ValueData { Alias { ty: Type, original: Value }, } +impl ValueData { + fn ty(&self) -> Type { + match *self { + ValueData::Inst { ty, .. } + | ValueData::Param { ty, .. } + | ValueData::Alias { ty, .. } => ty, + } + } +} + /// Instructions. /// impl DataFlowGraph { diff --git a/cranelift/codegen/src/ir/function.rs b/cranelift/codegen/src/ir/function.rs index 892af400af..f425eb669f 100644 --- a/cranelift/codegen/src/ir/function.rs +++ b/cranelift/codegen/src/ir/function.rs @@ -308,6 +308,30 @@ impl Function { // function, assume it is not a leaf. self.dfg.signatures.is_empty() } + + /// Replace the `dst` instruction's data with the `src` instruction's data + /// and then remove `src`. + /// + /// `src` and its result values should not be used at all, as any uses would + /// be left dangling after calling this method. + /// + /// `src` and `dst` must have the same number of resulting values, and + /// `src`'s i^th value must have the same type as `dst`'s i^th value. + pub fn transplant_inst(&mut self, dst: Inst, src: Inst) { + debug_assert_eq!( + self.dfg.inst_results(dst).len(), + self.dfg.inst_results(src).len() + ); + debug_assert!(self + .dfg + .inst_results(dst) + .iter() + .zip(self.dfg.inst_results(src)) + .all(|(a, b)| self.dfg.value_type(*a) == self.dfg.value_type(*b))); + + self.dfg[dst] = self.dfg[src].clone(); + self.layout.remove_inst(src); + } } /// Additional annotations for function display. diff --git a/cranelift/codegen/src/ir/instructions.rs b/cranelift/codegen/src/ir/instructions.rs index deb79130d4..1ada09fd71 100644 --- a/cranelift/codegen/src/ir/instructions.rs +++ b/cranelift/codegen/src/ir/instructions.rs @@ -11,9 +11,7 @@ use core::fmt::{self, Display, Formatter}; use core::ops::{Deref, DerefMut}; use core::str::FromStr; -use crate::ir; -use crate::ir::types; -use crate::ir::{Block, FuncRef, JumpTable, SigRef, Type, Value}; +use crate::ir::{self, trapcode::TrapCode, types, Block, FuncRef, JumpTable, SigRef, Type, Value}; use crate::isa; use crate::bitset::BitSet; @@ -257,6 +255,30 @@ impl InstructionData { } } + /// If this is a trapping instruction, get its trap code. Otherwise, return + /// `None`. + pub fn trap_code(&self) -> Option { + match *self { + Self::CondTrap { code, .. } + | Self::FloatCondTrap { code, .. } + | Self::IntCondTrap { code, .. } + | Self::Trap { code, .. } => Some(code), + _ => None, + } + } + + /// If this is a trapping instruction, get an exclusive reference to its + /// trap code. Otherwise, return `None`. + pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> { + match self { + Self::CondTrap { code, .. } + | Self::FloatCondTrap { code, .. } + | Self::IntCondTrap { code, .. } + | Self::Trap { code, .. } => Some(code), + _ => None, + } + } + /// Return information about a call instruction. /// /// Any instruction that can call another function reveals its call signature here. diff --git a/cranelift/codegen/src/lib.rs b/cranelift/codegen/src/lib.rs index efe0ea00c6..05c5583f5b 100644 --- a/cranelift/codegen/src/lib.rs +++ b/cranelift/codegen/src/lib.rs @@ -101,6 +101,7 @@ mod licm; mod nan_canonicalization; mod num_uses; mod partition_slice; +mod peepmatic; mod postopt; mod predicates; mod redundant_reload_remover; diff --git a/cranelift/codegen/src/peepmatic.rs b/cranelift/codegen/src/peepmatic.rs new file mode 100644 index 0000000000..f78f5ccd3e --- /dev/null +++ b/cranelift/codegen/src/peepmatic.rs @@ -0,0 +1,847 @@ +//! Glue for working with `peepmatic`-generated peephole optimizers. + +use crate::cursor::{Cursor, FuncCursor}; +use crate::ir::{ + dfg::DataFlowGraph, + entities::{Inst, Value}, + immediates::{Imm64, Uimm64}, + instructions::{InstructionData, Opcode}, + types, InstBuilder, +}; +use crate::isa::TargetIsa; +use cranelift_codegen_shared::condcodes::IntCC; +use peepmatic_runtime::{ + cc::ConditionCode, + instruction_set::InstructionSet, + operator::Operator, + part::{Constant, Part}, + paths::Path, + r#type::{BitWidth, Kind, Type}, + PeepholeOptimizations, PeepholeOptimizer, +}; +use std::boxed::Box; +use std::convert::{TryFrom, TryInto}; +use std::ptr; +use std::sync::atomic::{AtomicPtr, Ordering}; + +/// Get the `preopt.peepmatic` peephole optimizer. +pub(crate) fn preopt<'a, 'b>( + isa: &'b dyn TargetIsa, +) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> { + static SERIALIZED: &[u8] = include_bytes!("preopt.serialized"); + + // Once initialized, this must never be re-assigned. The initialized value + // is semantically "static data" and is intentionally leaked for the whole + // program's lifetime. + static DESERIALIZED: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + + // If `DESERIALIZED` has already been initialized, then just use it. + let ptr = DESERIALIZED.load(Ordering::SeqCst); + if let Some(peep_opts) = unsafe { ptr.as_ref() } { + return peep_opts.optimizer(isa); + } + + // Otherwise, if `DESERIALIZED` hasn't been initialized, then we need to + // deserialize the peephole optimizations and initialize it. However, + // another thread could be doing the same thing concurrently, so there is a + // race to see who initializes `DESERIALIZED` first, and we need to be + // prepared to both win or lose that race. + let peep_opts = PeepholeOptimizations::deserialize(SERIALIZED) + .expect("should always be able to deserialize `preopt.serialized`"); + let peep_opts = Box::into_raw(Box::new(peep_opts)); + + // Only update `DESERIALIZE` if it is still null, attempting to perform the + // one-time transition from null -> non-null. + if DESERIALIZED + .compare_and_swap(ptr::null_mut(), peep_opts, Ordering::SeqCst) + .is_null() + { + // We won the race to initialize `DESERIALIZED`. + debug_assert_eq!(DESERIALIZED.load(Ordering::SeqCst), peep_opts); + let peep_opts = unsafe { &*peep_opts }; + return peep_opts.optimizer(isa); + } + + // We lost the race to initialize `DESERIALIZED`. Drop our no-longer-needed + // instance of `peep_opts` and get the pointer to the instance that won the + // race. + let _ = unsafe { Box::from_raw(peep_opts) }; + let peep_opts = DESERIALIZED.load(Ordering::SeqCst); + let peep_opts = unsafe { peep_opts.as_ref().unwrap() }; + peep_opts.optimizer(isa) +} + +/// Either a `Value` or an `Inst`. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ValueOrInst { + Value(Value), + Inst(Inst), +} + +impl ValueOrInst { + /// Get the underlying `Value` if any. + pub fn value(&self) -> Option { + match *self { + Self::Value(v) => Some(v), + Self::Inst(_) => None, + } + } + + /// Get the underlying `Inst` if any. + pub fn inst(&self) -> Option { + match *self { + Self::Inst(i) => Some(i), + Self::Value(_) => None, + } + } + + /// Unwrap the underlying `Value`, panicking if it is not a `Value. + pub fn unwrap_value(&self) -> Value { + self.value().unwrap() + } + + /// Unwrap the underlying `Inst`, panicking if it is not a `Inst. + pub fn unwrap_inst(&self) -> Inst { + self.inst().unwrap() + } + + /// Is this a `Value`? + pub fn is_value(&self) -> bool { + self.value().is_some() + } + + /// Is this an `Inst`? + pub fn is_inst(&self) -> bool { + self.inst().is_some() + } + + fn resolve_inst(&self, dfg: &DataFlowGraph) -> Option { + match *self { + ValueOrInst::Inst(i) => Some(i), + ValueOrInst::Value(v) => dfg.value_def(v).inst(), + } + } + + fn result_bit_width(&self, dfg: &DataFlowGraph) -> u8 { + match *self { + ValueOrInst::Value(v) => dfg.value_type(v).bits().try_into().unwrap(), + ValueOrInst::Inst(inst) => { + let result = dfg.first_result(inst); + dfg.value_type(result).bits().try_into().unwrap() + } + } + } + + fn to_constant(&self, pos: &mut FuncCursor) -> Option { + let inst = self.resolve_inst(&pos.func.dfg)?; + match pos.func.dfg[inst] { + InstructionData::UnaryImm { + opcode: Opcode::Iconst, + imm, + } => { + let width = self.result_bit_width(&pos.func.dfg).try_into().unwrap(); + let x: i64 = imm.into(); + Some(Constant::Int(x as u64, width)) + } + InstructionData::UnaryBool { + opcode: Opcode::Bconst, + imm, + } => { + let width = self.result_bit_width(&pos.func.dfg).try_into().unwrap(); + Some(Constant::Bool(imm, width)) + } + _ => None, + } + } +} + +impl From for ValueOrInst { + fn from(v: Value) -> ValueOrInst { + ValueOrInst::Value(v) + } +} + +impl From for ValueOrInst { + fn from(i: Inst) -> ValueOrInst { + ValueOrInst::Inst(i) + } +} + +/// Get the fixed bit width of `bit_width`, or if it is polymorphic, the bit +/// width of `root`. +fn bit_width(dfg: &DataFlowGraph, bit_width: BitWidth, root: Inst) -> u8 { + bit_width.fixed_width().unwrap_or_else(|| { + let tyvar = dfg.ctrl_typevar(root); + let ty = dfg.compute_result_type(root, 0, tyvar).unwrap(); + u8::try_from(ty.bits()).unwrap() + }) +} + +/// Convert the constant `c` into an instruction. +fn const_to_value<'a>(builder: impl InstBuilder<'a>, c: Constant, root: Inst) -> Value { + match c { + Constant::Bool(b, width) => { + let width = bit_width(builder.data_flow_graph(), width, root); + let ty = match width { + 1 => types::B1, + 8 => types::B8, + 16 => types::B16, + 32 => types::B32, + 64 => types::B64, + 128 => types::B128, + _ => unreachable!(), + }; + builder.bconst(ty, b) + } + Constant::Int(x, width) => { + let width = bit_width(builder.data_flow_graph(), width, root); + let ty = match width { + 1 | 8 => types::I8, + 16 => types::I16, + 32 => types::I32, + 64 => types::I64, + 128 => types::I128, + _ => unreachable!(), + }; + builder.iconst(ty, x as i64) + } + } +} + +fn part_to_inst(pos: &mut FuncCursor, root: Inst, part: Part) -> Option { + match part { + Part::Instruction(ValueOrInst::Inst(inst)) => Some(inst), + Part::Instruction(ValueOrInst::Value(v)) => { + let inst = pos.func.dfg.value_def(v).inst()?; + if pos.func.dfg.inst_results(inst).len() == 1 { + Some(inst) + } else { + None + } + } + Part::Constant(c) => { + let v = const_to_value(pos.ins(), c, root); + let inst = pos.func.dfg.value_def(v).unwrap_inst(); + Some(inst) + } + Part::ConditionCode(_) => None, + } +} + +fn part_to_value(pos: &mut FuncCursor, root: Inst, part: Part) -> Option { + match part { + Part::Instruction(ValueOrInst::Inst(inst)) => { + pos.func.dfg.inst_results(inst).first().copied() + } + Part::Instruction(ValueOrInst::Value(v)) => Some(v), + Part::Constant(c) => Some(const_to_value(pos.ins(), c, root)), + Part::ConditionCode(_) => None, + } +} + +impl Opcode { + fn to_peepmatic_operator(&self) -> Option { + macro_rules! convert { + ( $( $op:ident $(,)* )* ) => { + match self { + $( Self::$op => Some(Operator::$op), )* + _ => None, + } + } + } + + convert!( + AdjustSpDown, + AdjustSpDownImm, + Band, + BandImm, + Bconst, + Bint, + Bor, + BorImm, + Brnz, + Brz, + Bxor, + BxorImm, + Iadd, + IaddImm, + Icmp, + IcmpImm, + Iconst, + Ifcmp, + IfcmpImm, + Imul, + ImulImm, + Ireduce, + IrsubImm, + Ishl, + IshlImm, + Isub, + Rotl, + RotlImm, + Rotr, + RotrImm, + Sdiv, + SdivImm, + Select, + Sextend, + Srem, + SremImm, + Sshr, + SshrImm, + Trapnz, + Trapz, + Udiv, + UdivImm, + Uextend, + Urem, + UremImm, + Ushr, + UshrImm, + ) + } +} + +impl TryFrom for Imm64 { + type Error = &'static str; + + fn try_from(c: Constant) -> Result { + match c { + Constant::Int(x, _) => Ok(Imm64::from(x as i64)), + Constant::Bool(..) => Err("cannot create Imm64 from Constant::Bool"), + } + } +} + +impl Into for Imm64 { + #[inline] + fn into(self) -> Constant { + let x: i64 = self.into(); + Constant::Int(x as _, BitWidth::SixtyFour) + } +} + +impl Into> for Imm64 { + #[inline] + fn into(self) -> Part { + let c: Constant = self.into(); + c.into() + } +} + +fn part_to_imm64(pos: &mut FuncCursor, part: Part) -> Imm64 { + return match part { + Part::Instruction(x) => match x.to_constant(pos).unwrap_or_else(|| cannot_convert()) { + Constant::Int(x, _) => (x as i64).into(), + Constant::Bool(..) => cannot_convert(), + }, + Part::Constant(Constant::Int(x, _)) => (x as i64).into(), + Part::ConditionCode(_) | Part::Constant(Constant::Bool(..)) => cannot_convert(), + }; + + #[inline(never)] + #[cold] + fn cannot_convert() -> ! { + panic!("cannot convert part into `Imm64`") + } +} + +impl Into for Uimm64 { + #[inline] + fn into(self) -> Constant { + let x: u64 = self.into(); + Constant::Int(x, BitWidth::SixtyFour) + } +} + +impl Into> for Uimm64 { + #[inline] + fn into(self) -> Part { + let c: Constant = self.into(); + c.into() + } +} + +fn peepmatic_to_intcc(cc: ConditionCode) -> IntCC { + match cc { + ConditionCode::Eq => IntCC::Equal, + ConditionCode::Ne => IntCC::NotEqual, + ConditionCode::Slt => IntCC::SignedLessThan, + ConditionCode::Sle => IntCC::SignedGreaterThanOrEqual, + ConditionCode::Sgt => IntCC::SignedGreaterThan, + ConditionCode::Sge => IntCC::SignedLessThanOrEqual, + ConditionCode::Ult => IntCC::UnsignedLessThan, + ConditionCode::Uge => IntCC::UnsignedGreaterThanOrEqual, + ConditionCode::Ugt => IntCC::UnsignedGreaterThan, + ConditionCode::Ule => IntCC::UnsignedLessThanOrEqual, + ConditionCode::Of => IntCC::Overflow, + ConditionCode::Nof => IntCC::NotOverflow, + } +} + +fn intcc_to_peepmatic(cc: IntCC) -> ConditionCode { + match cc { + IntCC::Equal => ConditionCode::Eq, + IntCC::NotEqual => ConditionCode::Ne, + IntCC::SignedLessThan => ConditionCode::Slt, + IntCC::SignedGreaterThanOrEqual => ConditionCode::Sle, + IntCC::SignedGreaterThan => ConditionCode::Sgt, + IntCC::SignedLessThanOrEqual => ConditionCode::Sge, + IntCC::UnsignedLessThan => ConditionCode::Ult, + IntCC::UnsignedGreaterThanOrEqual => ConditionCode::Uge, + IntCC::UnsignedGreaterThan => ConditionCode::Ugt, + IntCC::UnsignedLessThanOrEqual => ConditionCode::Ule, + IntCC::Overflow => ConditionCode::Of, + IntCC::NotOverflow => ConditionCode::Nof, + } +} + +fn get_immediate(dfg: &DataFlowGraph, inst: Inst, i: usize) -> Part { + return match dfg[inst] { + InstructionData::BinaryImm { imm, .. } if i == 0 => imm.into(), + InstructionData::BranchIcmp { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(), + InstructionData::BranchInt { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(), + InstructionData::IntCompare { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(), + InstructionData::IntCompareImm { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(), + InstructionData::IntCompareImm { imm, .. } if i == 1 => imm.into(), + InstructionData::IntCond { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(), + InstructionData::IntCondTrap { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(), + InstructionData::IntSelect { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(), + InstructionData::UnaryBool { imm, .. } if i == 0 => { + Constant::Bool(imm, BitWidth::Polymorphic).into() + } + InstructionData::UnaryImm { imm, .. } if i == 0 => imm.into(), + ref otherwise => unsupported(otherwise), + }; + + #[inline(never)] + #[cold] + fn unsupported(data: &InstructionData) -> ! { + panic!("unsupported instruction data: {:?}", data) + } +} + +fn get_argument(dfg: &DataFlowGraph, inst: Inst, i: usize) -> Option { + dfg.inst_args(inst).get(i).copied() +} + +fn peepmatic_ty_to_ir_ty(ty: Type, dfg: &DataFlowGraph, root: Inst) -> types::Type { + match (ty.kind, bit_width(dfg, ty.bit_width, root)) { + (Kind::Int, 1) | (Kind::Int, 8) => types::I8, + (Kind::Int, 16) => types::I16, + (Kind::Int, 32) => types::I32, + (Kind::Int, 64) => types::I64, + (Kind::Int, 128) => types::I128, + (Kind::Bool, 1) => types::B1, + (Kind::Bool, 8) => types::I8, + (Kind::Bool, 16) => types::I16, + (Kind::Bool, 32) => types::I32, + (Kind::Bool, 64) => types::I64, + (Kind::Bool, 128) => types::I128, + _ => unreachable!(), + } +} + +impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { + type Context = FuncCursor<'b>; + + type Instruction = ValueOrInst; + + fn replace_instruction( + &self, + pos: &mut FuncCursor<'b>, + old: ValueOrInst, + new: Part, + ) -> ValueOrInst { + log::trace!("replace {:?} with {:?}", old, new); + let old_inst = old.unwrap_inst(); + + // Try to convert `new` to an instruction, because we prefer replacing + // an old instruction with a new one wholesale. However, if the + // replacement cannot be converted to an instruction (e.g. the + // right-hand side is a block/function parameter value) then we change + // the old instruction's result to an alias of the new value. + match part_to_inst(pos, old_inst, new) { + Some(new_inst) => { + pos.func.transplant_inst(old_inst, new_inst); + debug_assert_eq!(pos.current_inst(), Some(old_inst)); + old_inst.into() + } + None => { + let new_value = part_to_value(pos, old_inst, new).unwrap(); + + let old_results = pos.func.dfg.detach_results(old_inst); + let old_results = old_results.as_slice(&pos.func.dfg.value_lists); + assert_eq!(old_results.len(), 1); + let old_value = old_results[0]; + + pos.func.dfg.change_to_alias(old_value, new_value); + pos.func.dfg.replace(old_inst).nop(); + + new_value.into() + } + } + } + + fn get_part_at_path( + &self, + pos: &mut FuncCursor<'b>, + root: ValueOrInst, + path: Path, + ) -> Option> { + // The root is path [0]. + debug_assert!(!path.0.is_empty()); + debug_assert_eq!(path.0[0], 0); + + let mut part = Part::Instruction(root); + for p in path.0[1..].iter().copied() { + let inst = part.as_instruction()?.resolve_inst(&pos.func.dfg)?; + let operator = pos.func.dfg[inst].opcode().to_peepmatic_operator()?; + + if p < operator.immediates_arity() { + part = get_immediate(&pos.func.dfg, inst, p as usize); + continue; + } + + let arg = p - operator.immediates_arity(); + let arg = arg as usize; + let value = get_argument(&pos.func.dfg, inst, arg)?; + part = Part::Instruction(value.into()); + } + + log::trace!("get_part_at_path({:?}) = {:?}", path, part); + Some(part) + } + + fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option { + let inst = value_or_inst.resolve_inst(&pos.func.dfg)?; + pos.func.dfg[inst].opcode().to_peepmatic_operator() + } + + fn make_inst_1( + &self, + pos: &mut FuncCursor<'b>, + root: ValueOrInst, + operator: Operator, + r#type: Type, + a: Part, + ) -> ValueOrInst { + log::trace!("make_inst_1: {:?}({:?})", operator, a); + + let root = root.unwrap_inst(); + match operator { + Operator::AdjustSpDown => { + let a = part_to_value(pos, root, a).unwrap(); + pos.ins().adjust_sp_down(a).into() + } + Operator::AdjustSpDownImm => { + let c = a.unwrap_constant(); + let imm = Imm64::try_from(c).unwrap(); + pos.ins().adjust_sp_down_imm(imm).into() + } + Operator::Bconst => { + let c = a.unwrap_constant(); + const_to_value(pos.ins(), c, root).into() + } + Operator::Bint => { + let a = part_to_value(pos, root, a).unwrap(); + let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); + pos.ins().bint(ty, a).into() + } + Operator::Brnz => { + let a = part_to_value(pos, root, a).unwrap(); + + // NB: branching instructions must be the root of an + // optimization's right-hand side, so we get the destination + // block and arguments from the left-hand side's root. Peepmatic + // doesn't currently represent labels or varargs. + let block = pos.func.dfg[root].branch_destination().unwrap(); + let args = pos.func.dfg.inst_args(root)[1..].to_vec(); + + pos.ins().brnz(a, block, &args).into() + } + Operator::Brz => { + let a = part_to_value(pos, root, a).unwrap(); + + // See the comment in the `Operator::Brnz` match argm. + let block = pos.func.dfg[root].branch_destination().unwrap(); + let args = pos.func.dfg.inst_args(root)[1..].to_vec(); + + pos.ins().brz(a, block, &args).into() + } + Operator::Iconst => { + let a = a.unwrap_constant(); + const_to_value(pos.ins(), a, root).into() + } + Operator::Ireduce => { + let a = part_to_value(pos, root, a).unwrap(); + let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); + pos.ins().ireduce(ty, a).into() + } + Operator::Sextend => { + let a = part_to_value(pos, root, a).unwrap(); + let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); + pos.ins().sextend(ty, a).into() + } + Operator::Trapnz => { + let a = part_to_value(pos, root, a).unwrap(); + + // NB: similar to branching instructions (see comment in the + // `Operator::Brnz` match arm) trapping instructions must be the + // root of an optimization's right-hand side, and we get the + // trap code from the root of the left-hand side. Peepmatic + // doesn't currently represent trap codes. + let code = pos.func.dfg[root].trap_code().unwrap(); + + pos.ins().trapnz(a, code).into() + } + Operator::Trapz => { + let a = part_to_value(pos, root, a).unwrap(); + // See comment in the `Operator::Trapnz` match arm. + let code = pos.func.dfg[root].trap_code().unwrap(); + pos.ins().trapz(a, code).into() + } + Operator::Uextend => { + let a = part_to_value(pos, root, a).unwrap(); + let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); + pos.ins().uextend(ty, a).into() + } + _ => unreachable!(), + } + } + + fn make_inst_2( + &self, + pos: &mut FuncCursor<'b>, + root: ValueOrInst, + operator: Operator, + _: Type, + a: Part, + b: Part, + ) -> ValueOrInst { + log::trace!("make_inst_2: {:?}({:?}, {:?})", operator, a, b); + + let root = root.unwrap_inst(); + match operator { + Operator::Band => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().band(a, b).into() + } + Operator::BandImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().band_imm(b, a).into() + } + Operator::Bor => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().bor(a, b).into() + } + Operator::BorImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().bor_imm(b, a).into() + } + Operator::Bxor => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().bxor(a, b).into() + } + Operator::BxorImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().bxor_imm(b, a).into() + } + Operator::Iadd => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().iadd(a, b).into() + } + Operator::IaddImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().iadd_imm(b, a).into() + } + Operator::Ifcmp => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().ifcmp(a, b).into() + } + Operator::IfcmpImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().ifcmp_imm(b, a).into() + } + Operator::Imul => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().imul(a, b).into() + } + Operator::ImulImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().imul_imm(b, a).into() + } + Operator::IrsubImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().irsub_imm(b, a).into() + } + Operator::Ishl => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().ishl(a, b).into() + } + Operator::IshlImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().ishl_imm(b, a).into() + } + Operator::Isub => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().isub(a, b).into() + } + Operator::Rotl => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().rotl(a, b).into() + } + Operator::RotlImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().rotl_imm(b, a).into() + } + Operator::Rotr => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().rotr(a, b).into() + } + Operator::RotrImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().rotr_imm(b, a).into() + } + Operator::Sdiv => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().sdiv(a, b).into() + } + Operator::SdivImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().sdiv_imm(b, a).into() + } + Operator::Srem => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().srem(a, b).into() + } + Operator::SremImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().srem_imm(b, a).into() + } + Operator::Sshr => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().sshr(a, b).into() + } + Operator::SshrImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().sshr_imm(b, a).into() + } + Operator::Udiv => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().udiv(a, b).into() + } + Operator::UdivImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().udiv_imm(b, a).into() + } + Operator::Urem => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().urem(a, b).into() + } + Operator::UremImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().urem_imm(b, a).into() + } + Operator::Ushr => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().ushr(a, b).into() + } + Operator::UshrImm => { + let a = part_to_imm64(pos, a); + let b = part_to_value(pos, root, b).unwrap(); + pos.ins().ushr_imm(b, a).into() + } + _ => unreachable!(), + } + } + + fn make_inst_3( + &self, + pos: &mut FuncCursor<'b>, + root: ValueOrInst, + operator: Operator, + _: Type, + a: Part, + b: Part, + c: Part, + ) -> ValueOrInst { + log::trace!("make_inst_3: {:?}({:?}, {:?}, {:?})", operator, a, b, c); + + let root = root.unwrap_inst(); + match operator { + Operator::Icmp => { + let cond = a.unwrap_condition_code(); + let cond = peepmatic_to_intcc(cond); + let b = part_to_value(pos, root, b).unwrap(); + let c = part_to_value(pos, root, c).unwrap(); + pos.ins().icmp(cond, b, c).into() + } + Operator::IcmpImm => { + let cond = a.unwrap_condition_code(); + let cond = peepmatic_to_intcc(cond); + let imm = part_to_imm64(pos, b); + let c = part_to_value(pos, root, c).unwrap(); + pos.ins().icmp_imm(cond, c, imm).into() + } + Operator::Select => { + let a = part_to_value(pos, root, a).unwrap(); + let b = part_to_value(pos, root, b).unwrap(); + let c = part_to_value(pos, root, c).unwrap(); + pos.ins().select(a, b, c).into() + } + _ => unreachable!(), + } + } + + fn instruction_to_constant( + &self, + pos: &mut FuncCursor<'b>, + value_or_inst: ValueOrInst, + ) -> Option { + value_or_inst.to_constant(pos) + } + + fn instruction_result_bit_width( + &self, + pos: &mut FuncCursor<'b>, + value_or_inst: ValueOrInst, + ) -> u8 { + value_or_inst.result_bit_width(&pos.func.dfg) + } + + fn native_word_size_in_bits(&self, _pos: &mut FuncCursor<'b>) -> u8 { + self.pointer_bits() + } +} diff --git a/cranelift/peepmatic/examples/preopt.peepmatic b/cranelift/codegen/src/preopt.peepmatic similarity index 97% rename from cranelift/peepmatic/examples/preopt.peepmatic rename to cranelift/codegen/src/preopt.peepmatic index 22523f0a01..604460dd57 100644 --- a/cranelift/peepmatic/examples/preopt.peepmatic +++ b/cranelift/codegen/src/preopt.peepmatic @@ -178,8 +178,8 @@ ;; Division and remainder by constants. ;; -;; Note that this section is incomplete, and a bunch of related optimizations -;; are still hand-coded in `simple_preopt.rs`. +;; TODO: this section is incomplete, and a bunch of related optimizations are +;; still hand-coded in `simple_preopt.rs`. ;; (Division by one is handled above.) diff --git a/cranelift/codegen/src/preopt.serialized b/cranelift/codegen/src/preopt.serialized new file mode 100644 index 0000000000..eadd89e8cd Binary files /dev/null and b/cranelift/codegen/src/preopt.serialized differ diff --git a/cranelift/codegen/src/simple_preopt.rs b/cranelift/codegen/src/simple_preopt.rs index 5090246cf1..a03c8219d2 100644 --- a/cranelift/codegen/src/simple_preopt.rs +++ b/cranelift/codegen/src/simple_preopt.rs @@ -10,13 +10,12 @@ use crate::divconst_magic_numbers::{MS32, MS64, MU32, MU64}; use crate::flowgraph::ControlFlowGraph; use crate::ir::{ condcodes::{CondCode, IntCC}, - dfg::ValueDef, - immediates, - instructions::{Opcode, ValueList}, - types::{I16, I32, I64, I8}, + instructions::Opcode, + types::{I32, I64}, Block, DataFlowGraph, Function, Inst, InstBuilder, InstructionData, Type, Value, }; use crate::isa::TargetIsa; +use crate::peepmatic::ValueOrInst; use crate::timing; #[inline] @@ -183,12 +182,8 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // U32 div by 1: identity // U32 rem by 1: zero - DivRemByConstInfo::DivU32(n1, 1) | DivRemByConstInfo::RemU32(n1, 1) => { - if is_rem { - pos.func.dfg.replace(inst).iconst(I32, 0); - } else { - replace_single_result_with_alias(&mut pos.func.dfg, inst, n1); - } + DivRemByConstInfo::DivU32(_, 1) | DivRemByConstInfo::RemU32(_, 1) => { + unreachable!("unsigned division and remainder by one is handled in `preopt.peepmatic`"); } // U32 div, rem by a power-of-2 @@ -203,7 +198,10 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso let mask = (1u64 << k) - 1; pos.func.dfg.replace(inst).band_imm(n1, mask as i64); } else { - pos.func.dfg.replace(inst).ushr_imm(n1, k as i64); + unreachable!( + "unsigned division by a power of two is handled in \ + `preopt.peepmatic`" + ); } } @@ -253,12 +251,8 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // U64 div by 1: identity // U64 rem by 1: zero - DivRemByConstInfo::DivU64(n1, 1) | DivRemByConstInfo::RemU64(n1, 1) => { - if is_rem { - pos.func.dfg.replace(inst).iconst(I64, 0); - } else { - replace_single_result_with_alias(&mut pos.func.dfg, inst, n1); - } + DivRemByConstInfo::DivU64(_, 1) | DivRemByConstInfo::RemU64(_, 1) => { + unreachable!("unsigned division and remainder by one is handled in `preopt.peepmatic`"); } // U64 div, rem by a power-of-2 @@ -273,7 +267,9 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso let mask = (1u64 << k) - 1; pos.func.dfg.replace(inst).band_imm(n1, mask as i64); } else { - pos.func.dfg.replace(inst).ushr_imm(n1, k as i64); + unreachable!( + "unsigned division by a power of two is handled in `preopt.peepmatic`" + ); } } @@ -326,12 +322,8 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // S32 div by 1: identity // S32 rem by 1: zero - DivRemByConstInfo::DivS32(n1, 1) | DivRemByConstInfo::RemS32(n1, 1) => { - if is_rem { - pos.func.dfg.replace(inst).iconst(I32, 0); - } else { - replace_single_result_with_alias(&mut pos.func.dfg, inst, n1); - } + DivRemByConstInfo::DivS32(_, 1) | DivRemByConstInfo::RemS32(_, 1) => { + unreachable!("signed division and remainder by one is handled in `preopt.peepmatic`"); } DivRemByConstInfo::DivS32(n1, d) | DivRemByConstInfo::RemS32(n1, d) => { @@ -401,12 +393,8 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // S64 div by 1: identity // S64 rem by 1: zero - DivRemByConstInfo::DivS64(n1, 1) | DivRemByConstInfo::RemS64(n1, 1) => { - if is_rem { - pos.func.dfg.replace(inst).iconst(I64, 0); - } else { - replace_single_result_with_alias(&mut pos.func.dfg, inst, n1); - } + DivRemByConstInfo::DivS64(_, 1) | DivRemByConstInfo::RemS64(_, 1) => { + unreachable!("division and remaineder by one are handled in `preopt.peepmatic`"); } DivRemByConstInfo::DivS64(n1, d) | DivRemByConstInfo::RemS64(n1, d) => { @@ -468,340 +456,6 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso } } -#[inline] -fn resolve_imm64_value(dfg: &DataFlowGraph, value: Value) -> Option { - if let ValueDef::Result(candidate_inst, _) = dfg.value_def(value) { - if let InstructionData::UnaryImm { - opcode: Opcode::Iconst, - imm, - } = dfg[candidate_inst] - { - return Some(imm); - } - } - None -} - -/// Try to transform [(x << N) >> N] into a (un)signed-extending move. -/// Returns true if the final instruction has been converted to such a move. -fn try_fold_extended_move( - pos: &mut FuncCursor, - inst: Inst, - opcode: Opcode, - arg: Value, - imm: immediates::Imm64, -) -> bool { - if let ValueDef::Result(arg_inst, _) = pos.func.dfg.value_def(arg) { - if let InstructionData::BinaryImm { - opcode: Opcode::IshlImm, - arg: prev_arg, - imm: prev_imm, - } = &pos.func.dfg[arg_inst] - { - if imm != *prev_imm { - return false; - } - - let dest_ty = pos.func.dfg.ctrl_typevar(inst); - if dest_ty != pos.func.dfg.ctrl_typevar(arg_inst) || !dest_ty.is_int() { - return false; - } - - let imm_bits: i64 = imm.into(); - let ireduce_ty = match (dest_ty.lane_bits() as i64).wrapping_sub(imm_bits) { - 8 => I8, - 16 => I16, - 32 => I32, - _ => return false, - }; - let ireduce_ty = ireduce_ty.by(dest_ty.lane_count()).unwrap(); - - // This becomes a no-op, since ireduce_ty has a smaller lane width than - // the argument type (also the destination type). - let arg = *prev_arg; - let narrower_arg = pos.ins().ireduce(ireduce_ty, arg); - - if opcode == Opcode::UshrImm { - pos.func.dfg.replace(inst).uextend(dest_ty, narrower_arg); - } else { - pos.func.dfg.replace(inst).sextend(dest_ty, narrower_arg); - } - return true; - } - } - false -} - -/// Apply basic simplifications. -/// -/// This folds constants with arithmetic to form `_imm` instructions, and other minor -/// simplifications. -/// -/// Doesn't apply some simplifications if the native word width (in bytes) is smaller than the -/// controlling type's width of the instruction. This would result in an illegal instruction that -/// would likely be expanded back into an instruction on smaller types with the same initial -/// opcode, creating unnecessary churn. -fn simplify(pos: &mut FuncCursor, inst: Inst, native_word_width: u32) { - match pos.func.dfg[inst] { - InstructionData::Binary { opcode, args } => { - if let Some(mut imm) = resolve_imm64_value(&pos.func.dfg, args[1]) { - let new_opcode = match opcode { - Opcode::Iadd => Opcode::IaddImm, - Opcode::Imul => Opcode::ImulImm, - Opcode::Sdiv => Opcode::SdivImm, - Opcode::Udiv => Opcode::UdivImm, - Opcode::Srem => Opcode::SremImm, - Opcode::Urem => Opcode::UremImm, - Opcode::Band => Opcode::BandImm, - Opcode::Bor => Opcode::BorImm, - Opcode::Bxor => Opcode::BxorImm, - Opcode::Rotl => Opcode::RotlImm, - Opcode::Rotr => Opcode::RotrImm, - Opcode::Ishl => Opcode::IshlImm, - Opcode::Ushr => Opcode::UshrImm, - Opcode::Sshr => Opcode::SshrImm, - Opcode::Isub => { - imm = imm.wrapping_neg(); - Opcode::IaddImm - } - Opcode::Ifcmp => Opcode::IfcmpImm, - _ => return, - }; - let ty = pos.func.dfg.ctrl_typevar(inst); - if ty.bytes() <= native_word_width { - pos.func - .dfg - .replace(inst) - .BinaryImm(new_opcode, ty, imm, args[0]); - - // Repeat for BinaryImm simplification. - simplify(pos, inst, native_word_width); - } - } else if let Some(imm) = resolve_imm64_value(&pos.func.dfg, args[0]) { - let new_opcode = match opcode { - Opcode::Iadd => Opcode::IaddImm, - Opcode::Imul => Opcode::ImulImm, - Opcode::Band => Opcode::BandImm, - Opcode::Bor => Opcode::BorImm, - Opcode::Bxor => Opcode::BxorImm, - Opcode::Isub => Opcode::IrsubImm, - _ => return, - }; - let ty = pos.func.dfg.ctrl_typevar(inst); - if ty.bytes() <= native_word_width { - pos.func - .dfg - .replace(inst) - .BinaryImm(new_opcode, ty, imm, args[1]); - } - } - } - - InstructionData::Unary { opcode, arg } => { - if let Opcode::AdjustSpDown = opcode { - if let Some(imm) = resolve_imm64_value(&pos.func.dfg, arg) { - // Note this works for both positive and negative immediate values. - pos.func.dfg.replace(inst).adjust_sp_down_imm(imm); - } - } - } - - InstructionData::BinaryImm { opcode, arg, imm } => { - let ty = pos.func.dfg.ctrl_typevar(inst); - - let mut arg = arg; - let mut imm = imm; - match opcode { - Opcode::IaddImm - | Opcode::ImulImm - | Opcode::BorImm - | Opcode::BandImm - | Opcode::BxorImm => { - // Fold binary_op(C2, binary_op(C1, x)) into binary_op(binary_op(C1, C2), x) - if let ValueDef::Result(arg_inst, _) = pos.func.dfg.value_def(arg) { - if let InstructionData::BinaryImm { - opcode: prev_opcode, - arg: prev_arg, - imm: prev_imm, - } = &pos.func.dfg[arg_inst] - { - if opcode == *prev_opcode && ty == pos.func.dfg.ctrl_typevar(arg_inst) { - let lhs: i64 = imm.into(); - let rhs: i64 = (*prev_imm).into(); - let new_imm = match opcode { - Opcode::BorImm => lhs | rhs, - Opcode::BandImm => lhs & rhs, - Opcode::BxorImm => lhs ^ rhs, - Opcode::IaddImm => lhs.wrapping_add(rhs), - Opcode::ImulImm => lhs.wrapping_mul(rhs), - _ => panic!("can't happen"), - }; - let new_imm = immediates::Imm64::from(new_imm); - let new_arg = *prev_arg; - pos.func - .dfg - .replace(inst) - .BinaryImm(opcode, ty, new_imm, new_arg); - imm = new_imm; - arg = new_arg; - } - } - } - } - - Opcode::UshrImm | Opcode::SshrImm => { - if pos.func.dfg.ctrl_typevar(inst).bytes() <= native_word_width - && try_fold_extended_move(pos, inst, opcode, arg, imm) - { - return; - } - } - - _ => {} - }; - - // Replace operations that are no-ops. - match (opcode, imm.into()) { - (Opcode::IaddImm, 0) - | (Opcode::ImulImm, 1) - | (Opcode::SdivImm, 1) - | (Opcode::UdivImm, 1) - | (Opcode::BorImm, 0) - | (Opcode::BandImm, -1) - | (Opcode::BxorImm, 0) - | (Opcode::RotlImm, 0) - | (Opcode::RotrImm, 0) - | (Opcode::IshlImm, 0) - | (Opcode::UshrImm, 0) - | (Opcode::SshrImm, 0) => { - // Alias the result value with the original argument. - replace_single_result_with_alias(&mut pos.func.dfg, inst, arg); - } - (Opcode::ImulImm, 0) | (Opcode::BandImm, 0) => { - // Replace by zero. - pos.func.dfg.replace(inst).iconst(ty, 0); - } - (Opcode::BorImm, -1) => { - // Replace by minus one. - pos.func.dfg.replace(inst).iconst(ty, -1); - } - _ => {} - } - } - - InstructionData::IntCompare { opcode, cond, args } => { - debug_assert_eq!(opcode, Opcode::Icmp); - if let Some(imm) = resolve_imm64_value(&pos.func.dfg, args[1]) { - if pos.func.dfg.ctrl_typevar(inst).bytes() <= native_word_width { - pos.func.dfg.replace(inst).icmp_imm(cond, args[0], imm); - } - } - } - - InstructionData::CondTrap { .. } - | InstructionData::Branch { .. } - | InstructionData::Ternary { - opcode: Opcode::Select, - .. - } => { - // Fold away a redundant `bint`. - let condition_def = { - let args = pos.func.dfg.inst_args(inst); - pos.func.dfg.value_def(args[0]) - }; - if let ValueDef::Result(def_inst, _) = condition_def { - if let InstructionData::Unary { - opcode: Opcode::Bint, - arg: bool_val, - } = pos.func.dfg[def_inst] - { - let args = pos.func.dfg.inst_args_mut(inst); - args[0] = bool_val; - } - } - } - - _ => {} - } -} - -struct BranchOptInfo { - br_inst: Inst, - cmp_arg: Value, - args: ValueList, - new_opcode: Opcode, -} - -/// Fold comparisons into branch operations when possible. -/// -/// This matches against operations which compare against zero, then use the -/// result in a `brz` or `brnz` branch. It folds those two operations into a -/// single `brz` or `brnz`. -fn branch_opt(pos: &mut FuncCursor, inst: Inst) { - let mut info = if let InstructionData::Branch { - opcode: br_opcode, - args: ref br_args, - .. - } = pos.func.dfg[inst] - { - let first_arg = { - let args = pos.func.dfg.inst_args(inst); - args[0] - }; - - let icmp_inst = if let ValueDef::Result(icmp_inst, _) = pos.func.dfg.value_def(first_arg) { - icmp_inst - } else { - return; - }; - - if let InstructionData::IntCompareImm { - opcode: Opcode::IcmpImm, - arg: cmp_arg, - cond: cmp_cond, - imm: cmp_imm, - } = pos.func.dfg[icmp_inst] - { - let cmp_imm: i64 = cmp_imm.into(); - if cmp_imm != 0 { - return; - } - - // icmp_imm returns non-zero when the comparison is true. So, if - // we're branching on zero, we need to invert the condition. - let cond = match br_opcode { - Opcode::Brz => cmp_cond.inverse(), - Opcode::Brnz => cmp_cond, - _ => return, - }; - - let new_opcode = match cond { - IntCC::Equal => Opcode::Brz, - IntCC::NotEqual => Opcode::Brnz, - _ => return, - }; - - BranchOptInfo { - br_inst: inst, - cmp_arg, - args: br_args.clone(), - new_opcode, - } - } else { - return; - } - } else { - return; - }; - - info.args.as_mut_slice(&mut pos.func.dfg.value_lists)[0] = info.cmp_arg; - if let InstructionData::Branch { ref mut opcode, .. } = pos.func.dfg[info.br_inst] { - *opcode = info.new_opcode; - } else { - panic!(); - } -} - enum BranchOrderKind { BrzToBrnz(Value), BrnzToBrz(Value), @@ -945,14 +599,20 @@ fn branch_order(pos: &mut FuncCursor, cfg: &mut ControlFlowGraph, block: Block, } /// The main pre-opt pass. -pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) { +pub fn do_preopt<'func, 'isa>( + func: &'func mut Function, + cfg: &mut ControlFlowGraph, + isa: &'isa dyn TargetIsa, +) { let _tt = timing::preopt(); + let mut pos = FuncCursor::new(func); - let native_word_width = isa.pointer_bytes(); + let mut preopt = crate::peepmatic::preopt(isa); + while let Some(block) = pos.next_block() { while let Some(inst) = pos.next_inst() { - // Apply basic simplifications. - simplify(&mut pos, inst, native_word_width as u32); + preopt.apply_all(&mut pos, ValueOrInst::Inst(inst)); + let inst = pos.current_inst().unwrap(); // Try to transform divide-by-constant into simpler operations. if let Some(divrem_info) = get_div_info(inst, &pos.func.dfg) { @@ -960,7 +620,6 @@ pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph, isa: &dyn Targ continue; } - branch_opt(&mut pos, inst); branch_order(&mut pos, cfg, block, inst); } } diff --git a/cranelift/filetests/filetests/isa/x86/isub_imm-i8.clif b/cranelift/filetests/filetests/isa/x86/isub_imm-i8.clif index 018ac95fbc..e59226c7de 100644 --- a/cranelift/filetests/filetests/isa/x86/isub_imm-i8.clif +++ b/cranelift/filetests/filetests/isa/x86/isub_imm-i8.clif @@ -6,9 +6,9 @@ function u0:0(i8) -> i8 fast { block0(v0: i8): v1 = iconst.i8 0 v2 = isub v1, v0 - ; check: v3 = uextend.i32 v0 - ; nextln: v5 = iconst.i32 0 - ; nextln = isub v5, v3 - ; nextln = ireduce.i8 v4 + ; check: v4 = uextend.i32 v0 + ; nextln: v6 = iconst.i32 0 + ; nextln: v5 = isub v6, v4 + ; nextln: v2 = ireduce.i8 v5 return v2 } diff --git a/cranelift/filetests/filetests/simple_preopt/div_by_const_indirect.clif b/cranelift/filetests/filetests/simple_preopt/div_by_const_indirect.clif index 101e4eb201..4a4b7a80b6 100644 --- a/cranelift/filetests/filetests/simple_preopt/div_by_const_indirect.clif +++ b/cranelift/filetests/filetests/simple_preopt/div_by_const_indirect.clif @@ -7,14 +7,13 @@ function %indir_udiv32(i32) -> i32 { block0(v0: i32): v1 = iconst.i32 7 v2 = udiv v0, v1 - ; check: iconst.i32 7 - ; check: iconst.i32 0x2492_4925 - ; check: umulhi v0, v3 - ; check: isub v0, v4 - ; check: ushr_imm v5, 1 - ; check: iadd v6, v4 - ; check: v8 = ushr_imm v7, 2 - ; check: v2 -> v8 + ; check: v4 = iconst.i32 0x2492_4925 + ; nextln: v5 = umulhi v0, v4 + ; nextln: v6 = isub v0, v5 + ; nextln: v7 = ushr_imm v6, 1 + ; nextln: v8 = iadd v7, v5 + ; nextln: v9 = ushr_imm v8, 2 + ; nextln: v2 -> v9 return v2 } @@ -22,13 +21,12 @@ function %indir_sdiv32(i32) -> i32 { block0(v0: i32): v1 = iconst.i32 -17 v2 = sdiv v0, v1 - ; check: iconst.i32 -17 - ; check: iconst.i32 0xffff_ffff_8787_8787 - ; check: smulhi v0, v3 - ; check: sshr_imm v4, 3 - ; check: ushr_imm v5, 31 - ; check: v7 = iadd v5, v6 - ; check: v2 -> v7 + ; check: v4 = iconst.i32 0xffff_ffff_8787_8787 + ; nextln: v5 = smulhi v0, v4 + ; nextln: v6 = sshr_imm v5, 3 + ; nextln: v7 = ushr_imm v6, 31 + ; nextln: v8 = iadd v6, v7 + ; nextln: v2 -> v8 return v2 } @@ -36,11 +34,10 @@ function %indir_udiv64(i64) -> i64 { block0(v0: i64): v1 = iconst.i64 1337 v2 = udiv v0, v1 - ; check: iconst.i64 1337 - ; check: iconst.i64 0xc411_9d95_2866_a139 - ; check: umulhi v0, v3 - ; check: v5 = ushr_imm v4, 10 - ; check: v2 -> v5 + ; check: v4 = iconst.i64 0xc411_9d95_2866_a139 + ; nextln: v5 = umulhi v0, v4 + ; nextln: v6 = ushr_imm v5, 10 + ; nextln: v2 -> v6 return v2 } @@ -48,12 +45,11 @@ function %indir_sdiv64(i64) -> i64 { block0(v0: i64): v1 = iconst.i64 -90210 v2 = sdiv v0, v1 - ; check: iconst.i64 0xffff_ffff_fffe_9f9e - ; check: iconst.i64 0xd181_4ee8_939c_b8bb - ; check: smulhi v0, v3 - ; check: sshr_imm v4, 14 - ; check: ushr_imm v5, 63 - ; check: v7 = iadd v5, v6 - ; check: v2 -> v7 + ; check: v4 = iconst.i64 0xd181_4ee8_939c_b8bb + ; nextln: v5 = smulhi v0, v4 + ; nextln: v6 = sshr_imm v5, 14 + ; nextln: v7 = ushr_imm v6, 63 + ; nextln: v8 = iadd v6, v7 + ; nextln: v2 -> v8 return v2 } diff --git a/cranelift/filetests/filetests/simple_preopt/replace_branching_instructions_and_cfg_predecessors.clif b/cranelift/filetests/filetests/simple_preopt/replace_branching_instructions_and_cfg_predecessors.clif new file mode 100644 index 0000000000..702896c22d --- /dev/null +++ b/cranelift/filetests/filetests/simple_preopt/replace_branching_instructions_and_cfg_predecessors.clif @@ -0,0 +1,22 @@ +test simple_preopt +target x86_64 + +function u0:2(i64 , i64) { + gv1 = load.i64 notrap aligned gv0 + heap0 = static gv1 + block0(v0: i64, v1: i64): + v16 = iconst.i32 6 + v17 = heap_addr.i64 heap0, v16, 1 + v18 = load.i32 v17 + v19 = iconst.i32 4 + v20 = icmp ne v18, v19 + v21 = bint.i32 v20 + brnz v21, block2 + jump block4 + block4: + jump block1 + block2: + jump block1 + block1: + return +} diff --git a/cranelift/filetests/filetests/simple_preopt/simplify32.clif b/cranelift/filetests/filetests/simple_preopt/simplify32.clif index 2582fd69aa..cf238fb5ed 100644 --- a/cranelift/filetests/filetests/simple_preopt/simplify32.clif +++ b/cranelift/filetests/filetests/simple_preopt/simplify32.clif @@ -58,4 +58,3 @@ block0(v0: i64): ; nextln: v2 = iadd v0, v1 ; nextln: return v2 ; nextln: } - diff --git a/cranelift/filetests/filetests/simple_preopt/simplify64.clif b/cranelift/filetests/filetests/simple_preopt/simplify64.clif index 4ceabdc335..6489c3bd1e 100644 --- a/cranelift/filetests/filetests/simple_preopt/simplify64.clif +++ b/cranelift/filetests/filetests/simple_preopt/simplify64.clif @@ -44,6 +44,37 @@ block0(v0: i32): ; nextln: return v3 ; nextln: } +function %ifcmp_imm(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 2 + v2 = ifcmp v0, v1 + brif eq v2, block1 + jump block2 + +block1: + v3 = iconst.i32 1 + return v3 + +block2: + v4 = iconst.i32 2 + return v4 +} +; sameln: function %ifcmp_imm +; nextln: block0(v0: i32): +; nextln: v1 = iconst.i32 2 +; nextln: v2 = ifcmp_imm v0, 2 +; nextln: brif eq v2, block1 +; nextln: jump block2 +; nextln: +; nextln: block1: +; nextln: v3 = iconst.i32 1 +; nextln: return v3 +; nextln: +; nextln: block2: +; nextln: v4 = iconst.i32 2 +; nextln: return v4 +; nextln: } + function %brz_bint(i32) { block0(v0: i32): v3 = icmp_imm slt v0, 0 diff --git a/cranelift/filetests/filetests/simple_preopt/simplify_instruction_into_alias_of_value.clif b/cranelift/filetests/filetests/simple_preopt/simplify_instruction_into_alias_of_value.clif new file mode 100644 index 0000000000..076d7b17b5 --- /dev/null +++ b/cranelift/filetests/filetests/simple_preopt/simplify_instruction_into_alias_of_value.clif @@ -0,0 +1,17 @@ +test simple_preopt +target x86_64 + +;; The `isub` is a no-op, but we can't replace the whole `isub` instruction with +;; its `v2` operand's instruction because `v2` is one of many results. Instead, +;; we need to make an alias `v3 -> v2`. + +function %replace_inst_with_alias() -> i32 { +block0: + v0 = iconst.i32 0 + v1, v2 = x86_smulx v0, v0 + v3 = isub v2, v0 + ; check: v0 = iconst.i32 0 + ; nextln: v1, v2 = x86_smulx v0, v0 + ; nextln: v3 -> v2 + return v3 +} diff --git a/cranelift/filetests/src/test_simple_preopt.rs b/cranelift/filetests/src/test_simple_preopt.rs index 286a86ba23..1463d1c69a 100644 --- a/cranelift/filetests/src/test_simple_preopt.rs +++ b/cranelift/filetests/src/test_simple_preopt.rs @@ -38,6 +38,7 @@ impl SubTest for TestSimplePreopt { .preopt(isa) .map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?; let text = &comp_ctx.func.display(isa).to_string(); + log::debug!("After simple_preopt:\n{}", text); run_filecheck(&text, context) } } diff --git a/cranelift/peepmatic/src/lib.rs b/cranelift/peepmatic/src/lib.rs index 0cf4147db8..407ba3670d 100755 --- a/cranelift/peepmatic/src/lib.rs +++ b/cranelift/peepmatic/src/lib.rs @@ -160,6 +160,6 @@ mod tests { #[test] fn compile_preopt() { - assert_compiles("examples/preopt.peepmatic"); + assert_compiles("../codegen/src/preopt.peepmatic"); } }