cranelift: Port most of simple_preopt.rs over to the peepmatic DSL

This ports all of the identity, no-op, simplification, and canonicalization
related optimizations over from being hand-coded to the `peepmatic` DSL. This
does not handle the branch-to-branch optimizations or most of the
divide-by-constant optimizations.
This commit is contained in:
Nick Fitzgerald
2020-04-28 16:43:32 -07:00
parent 18663fede9
commit 090d1c2d32
20 changed files with 1289 additions and 488 deletions

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ docs/book
rusty-tags.* rusty-tags.*
tags tags
target target
.z3-trace

294
Cargo.lock generated
View File

@@ -11,9 +11,12 @@ dependencies = [
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.3.3" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" checksum = "0989268a37e128d4d7a8028f1c60099430113fdbc70419010601ce51a228e4fe"
dependencies = [
"const-random",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
@@ -41,9 +44,9 @@ checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
[[package]] [[package]]
name = "arbitrary" name = "arbitrary"
version = "0.4.4" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5eb01a9ab8a3369f2f7632b9461c34f5920bd454774bab5b9fc6744f21d6143" checksum = "75153c95fdedd7db9732dfbfc3702324a1627eec91ba56e37cd0ac78314ab2ed"
dependencies = [ dependencies = [
"derive_arbitrary", "derive_arbitrary",
] ]
@@ -97,9 +100,9 @@ dependencies = [
[[package]] [[package]]
name = "backtrace-sys" name = "backtrace-sys"
version = "0.1.36" version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7" checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -201,6 +204,12 @@ dependencies = [
"byte-tools", "byte-tools",
] ]
[[package]]
name = "bumpalo"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187"
[[package]] [[package]]
name = "byte-tools" name = "byte-tools"
version = "0.3.1" version = "0.3.1"
@@ -233,9 +242,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.52" version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
dependencies = [ dependencies = [
"jobserver", "jobserver",
] ]
@@ -261,6 +270,18 @@ dependencies = [
"vec_map", "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]] [[package]]
name = "cloudabi" name = "cloudabi"
version = "0.0.3" version = "0.0.3"
@@ -281,19 +302,18 @@ dependencies = [
[[package]] [[package]]
name = "console" name = "console"
version = "0.11.2" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea0f3e2e8d7dba335e913b97f9e1992c86c4399d54f8be1d31c8727d0652064" checksum = "6728a28023f207181b193262711102bfbaf47cc9d13bc71d0736607ef8efe88c"
dependencies = [ dependencies = [
"clicolors-control",
"encode_unicode", "encode_unicode",
"lazy_static", "lazy_static",
"libc", "libc",
"regex", "regex",
"terminal_size",
"termios", "termios",
"unicode-width", "unicode-width",
"winapi", "winapi",
"winapi-util",
] ]
[[package]] [[package]]
@@ -357,8 +377,10 @@ dependencies = [
"cranelift-codegen-shared", "cranelift-codegen-shared",
"cranelift-entity", "cranelift-entity",
"gimli", "gimli",
"hashbrown 0.7.2", "hashbrown 0.7.1",
"log", "log",
"peepmatic",
"peepmatic-runtime",
"regalloc", "regalloc",
"serde", "serde",
"smallvec", "smallvec",
@@ -424,7 +446,7 @@ name = "cranelift-frontend"
version = "0.63.0" version = "0.63.0"
dependencies = [ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"hashbrown 0.7.2", "hashbrown 0.7.1",
"log", "log",
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
@@ -438,7 +460,7 @@ dependencies = [
"cranelift-entity", "cranelift-entity",
"cranelift-frontend", "cranelift-frontend",
"cranelift-reader", "cranelift-reader",
"hashbrown 0.7.2", "hashbrown 0.7.1",
"log", "log",
"pretty_env_logger", "pretty_env_logger",
"thiserror", "thiserror",
@@ -566,12 +588,12 @@ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"cranelift-entity", "cranelift-entity",
"cranelift-frontend", "cranelift-frontend",
"hashbrown 0.7.2", "hashbrown 0.7.1",
"log", "log",
"serde", "serde",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmparser", "wasmparser 0.52.2",
"wat", "wat",
] ]
@@ -642,13 +664,13 @@ dependencies = [
[[package]] [[package]]
name = "derive_arbitrary" name = "derive_arbitrary"
version = "0.4.4" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cee758ebd1c79a9c6fb95f242dcc30bdbf555c28369ae908d21fdaf81537496" checksum = "caedd6a71b6d00bdc458ec8ffbfd12689c1ee7ffa69ad9933310aaf2f08f18d8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote",
"syn", "syn",
"synstructure",
] ]
[[package]] [[package]]
@@ -877,6 +899,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
[[package]]
name = "fst"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81f9cac32c1741cdf6b66be7dcf0d9c7f25ccf12f8aa84c16cfa31f9f14513b3"
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@@ -952,11 +980,11 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.7.2" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" checksum = "479e9d9a1a3f8c489868a935b557ab5710e3e223836da2ecd52901d88935cb56"
dependencies = [ dependencies = [
"ahash 0.3.3", "ahash 0.3.2",
"autocfg 1.0.0", "autocfg 1.0.0",
] ]
@@ -971,9 +999,9 @@ dependencies = [
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.12" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -1100,7 +1128,7 @@ dependencies = [
"staticvec", "staticvec",
"thiserror", "thiserror",
"typemap", "typemap",
"wasmparser", "wasmparser 0.52.2",
"wat", "wat",
] ]
@@ -1170,9 +1198,9 @@ dependencies = [
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
@@ -1226,6 +1254,76 @@ dependencies = [
"stable_deref_trait", "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]] [[package]]
name = "plain" name = "plain"
version = "0.2.3" version = "0.2.3"
@@ -1291,9 +1389,9 @@ dependencies = [
[[package]] [[package]]
name = "proptest" name = "proptest"
version = "0.9.6" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c477819b845fe023d33583ebf10c9f62518c8d79a0960ba5c36d6ac8a55a5b" checksum = "bf6147d103a7c9d7598f4105cf049b15c99e2ecd93179bf024f0fd349be5ada4"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"bitflags", "bitflags",
@@ -1350,7 +1448,7 @@ dependencies = [
"rand_isaac", "rand_isaac",
"rand_jitter", "rand_jitter",
"rand_os", "rand_os",
"rand_pcg", "rand_pcg 0.1.2",
"rand_xorshift", "rand_xorshift",
"winapi", "winapi",
] ]
@@ -1366,6 +1464,7 @@ dependencies = [
"rand_chacha 0.2.2", "rand_chacha 0.2.2",
"rand_core 0.5.1", "rand_core 0.5.1",
"rand_hc 0.2.0", "rand_hc 0.2.0",
"rand_pcg 0.2.1",
] ]
[[package]] [[package]]
@@ -1474,6 +1573,15 @@ dependencies = [
"rand_core 0.4.2", "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]] [[package]]
name = "rand_xorshift" name = "rand_xorshift"
version = "0.1.1" version = "0.1.1"
@@ -1557,9 +1665,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.3.7" version = "1.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -1648,9 +1756,9 @@ dependencies = [
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.4" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
[[package]] [[package]]
name = "same-file" name = "same-file"
@@ -1724,9 +1832,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.52" version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -1747,9 +1855,9 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.4.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
@@ -1780,9 +1888,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "structopt" name = "structopt"
version = "0.3.14" version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" checksum = "ff6da2e8d107dfd7b74df5ef4d205c6aebee0706c647f6bc6a2d5789905c00fb"
dependencies = [ dependencies = [
"clap", "clap",
"lazy_static", "lazy_static",
@@ -1791,9 +1899,9 @@ dependencies = [
[[package]] [[package]]
name = "structopt-derive" name = "structopt-derive"
version = "0.4.7" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" checksum = "a489c87c08fbaf12e386665109dd13470dcc9c4583ea3e10dd2b4523e5ebd9ac"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@@ -1804,9 +1912,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.18" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1824,6 +1932,18 @@ dependencies = [
"syn", "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]] [[package]]
name = "target-lexicon" name = "target-lexicon"
version = "0.10.0" version = "0.10.0"
@@ -1863,16 +1983,6 @@ dependencies = [
"winapi-util", "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]] [[package]]
name = "termios" name = "termios"
version = "0.3.2" version = "0.3.2"
@@ -1909,18 +2019,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.16" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60" checksum = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.16" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269" checksum = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1962,9 +2072,9 @@ dependencies = [
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.12.0" version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
@@ -2051,6 +2161,12 @@ dependencies = [
"yanix", "yanix",
] ]
[[package]]
name = "wasmparser"
version = "0.51.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a"
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.52.2" version = "0.52.2"
@@ -2059,12 +2175,12 @@ checksum = "733954023c0b39602439e60a65126fd31b003196d3a1e8e4531b055165a79b31"
[[package]] [[package]]
name = "wasmprinter" name = "wasmprinter"
version = "0.2.4" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21c3ac16b1f882bf1e1ce007e4f194559d2e3332013367863ddfbc828d1f044f" checksum = "8bd423d45b95fcee11775472bfdce66c63c45ada23c1b338e0a63d623a6c475b"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"wasmparser", "wasmparser 0.51.4",
] ]
[[package]] [[package]]
@@ -2080,7 +2196,7 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
"target-lexicon", "target-lexicon",
"tempfile", "tempfile",
"wasmparser", "wasmparser 0.52.2",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-jit", "wasmtime-jit",
"wasmtime-profiling", "wasmtime-profiling",
@@ -2150,7 +2266,7 @@ dependencies = [
"more-asserts", "more-asserts",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmparser", "wasmparser 0.52.2",
"wasmtime-environ", "wasmtime-environ",
] ]
@@ -2181,7 +2297,7 @@ dependencies = [
"tempfile", "tempfile",
"thiserror", "thiserror",
"toml", "toml",
"wasmparser", "wasmparser 0.52.2",
"winapi", "winapi",
"zstd", "zstd",
] ]
@@ -2194,6 +2310,7 @@ dependencies = [
"cranelift-reader", "cranelift-reader",
"cranelift-wasm", "cranelift-wasm",
"libfuzzer-sys", "libfuzzer-sys",
"peepmatic-fuzzing",
"target-lexicon", "target-lexicon",
"wasmtime", "wasmtime",
"wasmtime-fuzzing", "wasmtime-fuzzing",
@@ -2209,7 +2326,7 @@ dependencies = [
"env_logger 0.7.1", "env_logger 0.7.1",
"log", "log",
"rayon", "rayon",
"wasmparser", "wasmparser 0.52.2",
"wasmprinter", "wasmprinter",
"wasmtime", "wasmtime",
"wasmtime-wast", "wasmtime-wast",
@@ -2233,7 +2350,7 @@ dependencies = [
"region", "region",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmparser", "wasmparser 0.52.2",
"wasmtime-debug", "wasmtime-debug",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-profiling", "wasmtime-profiling",
@@ -2336,6 +2453,15 @@ dependencies = [
"leb128", "leb128",
] ]
[[package]]
name = "wast"
version = "13.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b20abd8b4a26f7e0d4dd5e357e90a3d555ec190e94472c9b2b27c5b9777f9ae"
dependencies = [
"leb128",
]
[[package]] [[package]]
name = "wast" name = "wast"
version = "15.0.0" version = "15.0.0"
@@ -2347,11 +2473,11 @@ dependencies = [
[[package]] [[package]]
name = "wat" name = "wat"
version = "1.0.16" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "526d28df6c047d9f9a92d4925b98afd8d8d95b1b3aa4f13eb1306f17d1da56c4" checksum = "51a615830ee3e7200b505c441fec09aac2f114deae69df52f215cb828ba112c4"
dependencies = [ dependencies = [
"wast 15.0.0", "wast 13.0.0",
] ]
[[package]] [[package]]
@@ -2424,9 +2550,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.5" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e"
dependencies = [ dependencies = [
"winapi", "winapi",
] ]
@@ -2470,6 +2596,26 @@ dependencies = [
"log", "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]] [[package]]
name = "zstd" name = "zstd"
version = "0.5.1+zstd.1.4.4" version = "0.5.1+zstd.1.4.4"

View File

@@ -24,6 +24,7 @@ gimli = { version = "0.20.0", default-features = false, features = ["write"], op
smallvec = { version = "1.0.0" } smallvec = { version = "1.0.0" }
thiserror = "1.0.4" thiserror = "1.0.4"
byteorder = { version = "1.3.2", default-features = false } byteorder = { version = "1.3.2", default-features = false }
peepmatic-runtime = { path = "../peepmatic/crates/runtime" }
regalloc = "0.0.23" regalloc = "0.0.23"
# It is a goal of the cranelift-codegen crate to have minimal external dependencies. # 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 # 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] [build-dependencies]
cranelift-codegen-meta = { path = "meta", version = "0.63.0" } cranelift-codegen-meta = { path = "meta", version = "0.63.0" }
peepmatic = { path = "../peepmatic", optional = true }
[features] [features]
default = ["std", "unwind"] default = ["std", "unwind"]
@@ -72,5 +74,9 @@ all-arch = [
# For dependent crates that want to serialize some parts of cranelift # For dependent crates that want to serialize some parts of cranelift
enable-serde = ["serde"] 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] [badges]
maintenance = { status = "experimental" } maintenance = { status = "experimental" }

View File

@@ -71,4 +71,22 @@ fn main() {
); );
println!("cargo:warning=Generated files are in {}", out_dir); 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`");
} }

View File

@@ -234,11 +234,7 @@ impl DataFlowGraph {
/// Get the type of a value. /// Get the type of a value.
pub fn value_type(&self, v: Value) -> Type { pub fn value_type(&self, v: Value) -> Type {
match self.values[v] { self.values[v].ty()
ValueData::Inst { ty, .. }
| ValueData::Param { ty, .. }
| ValueData::Alias { ty, .. } => ty,
}
} }
/// Get the definition of a value. /// Get the definition of a value.
@@ -383,9 +379,14 @@ pub enum ValueDef {
impl ValueDef { impl ValueDef {
/// Unwrap the instruction where the value was defined, or panic. /// Unwrap the instruction where the value was defined, or panic.
pub fn unwrap_inst(&self) -> Inst { 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<Inst> {
match *self { match *self {
Self::Result(inst, _) => inst, Self::Result(inst, _) => Some(inst),
_ => panic!("Value is not an instruction result"), _ => None,
} }
} }
@@ -428,6 +429,16 @@ enum ValueData {
Alias { ty: Type, original: Value }, Alias { ty: Type, original: Value },
} }
impl ValueData {
fn ty(&self) -> Type {
match *self {
ValueData::Inst { ty, .. }
| ValueData::Param { ty, .. }
| ValueData::Alias { ty, .. } => ty,
}
}
}
/// Instructions. /// Instructions.
/// ///
impl DataFlowGraph { impl DataFlowGraph {

View File

@@ -308,6 +308,30 @@ impl Function {
// function, assume it is not a leaf. // function, assume it is not a leaf.
self.dfg.signatures.is_empty() 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. /// Additional annotations for function display.

View File

@@ -11,9 +11,7 @@ use core::fmt::{self, Display, Formatter};
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::str::FromStr; use core::str::FromStr;
use crate::ir; use crate::ir::{self, trapcode::TrapCode, types, Block, FuncRef, JumpTable, SigRef, Type, Value};
use crate::ir::types;
use crate::ir::{Block, FuncRef, JumpTable, SigRef, Type, Value};
use crate::isa; use crate::isa;
use crate::bitset::BitSet; 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<TrapCode> {
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. /// Return information about a call instruction.
/// ///
/// Any instruction that can call another function reveals its call signature here. /// Any instruction that can call another function reveals its call signature here.

View File

@@ -101,6 +101,7 @@ mod licm;
mod nan_canonicalization; mod nan_canonicalization;
mod num_uses; mod num_uses;
mod partition_slice; mod partition_slice;
mod peepmatic;
mod postopt; mod postopt;
mod predicates; mod predicates;
mod redundant_reload_remover; mod redundant_reload_remover;

View File

@@ -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<PeepholeOptimizations> = 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<Value> {
match *self {
Self::Value(v) => Some(v),
Self::Inst(_) => None,
}
}
/// Get the underlying `Inst` if any.
pub fn inst(&self) -> Option<Inst> {
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<Inst> {
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<Constant> {
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<Value> for ValueOrInst {
fn from(v: Value) -> ValueOrInst {
ValueOrInst::Value(v)
}
}
impl From<Inst> 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<ValueOrInst>) -> Option<Inst> {
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<ValueOrInst>) -> Option<Value> {
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<Operator> {
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<Constant> for Imm64 {
type Error = &'static str;
fn try_from(c: Constant) -> Result<Self, Self::Error> {
match c {
Constant::Int(x, _) => Ok(Imm64::from(x as i64)),
Constant::Bool(..) => Err("cannot create Imm64 from Constant::Bool"),
}
}
}
impl Into<Constant> for Imm64 {
#[inline]
fn into(self) -> Constant {
let x: i64 = self.into();
Constant::Int(x as _, BitWidth::SixtyFour)
}
}
impl Into<Part<ValueOrInst>> for Imm64 {
#[inline]
fn into(self) -> Part<ValueOrInst> {
let c: Constant = self.into();
c.into()
}
}
fn part_to_imm64(pos: &mut FuncCursor, part: Part<ValueOrInst>) -> 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<Constant> for Uimm64 {
#[inline]
fn into(self) -> Constant {
let x: u64 = self.into();
Constant::Int(x, BitWidth::SixtyFour)
}
}
impl Into<Part<ValueOrInst>> for Uimm64 {
#[inline]
fn into(self) -> Part<ValueOrInst> {
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<ValueOrInst> {
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<Value> {
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>,
) -> 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<Part<ValueOrInst>> {
// 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<Operator> {
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>,
) -> 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<ValueOrInst>,
b: Part<ValueOrInst>,
) -> 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<ValueOrInst>,
b: Part<ValueOrInst>,
c: Part<ValueOrInst>,
) -> 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<Constant> {
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()
}
}

View File

@@ -178,8 +178,8 @@
;; Division and remainder by constants. ;; Division and remainder by constants.
;; ;;
;; Note that this section is incomplete, and a bunch of related optimizations ;; TODO: this section is incomplete, and a bunch of related optimizations are
;; are still hand-coded in `simple_preopt.rs`. ;; still hand-coded in `simple_preopt.rs`.
;; (Division by one is handled above.) ;; (Division by one is handled above.)

Binary file not shown.

View File

@@ -10,13 +10,12 @@ use crate::divconst_magic_numbers::{MS32, MS64, MU32, MU64};
use crate::flowgraph::ControlFlowGraph; use crate::flowgraph::ControlFlowGraph;
use crate::ir::{ use crate::ir::{
condcodes::{CondCode, IntCC}, condcodes::{CondCode, IntCC},
dfg::ValueDef, instructions::Opcode,
immediates, types::{I32, I64},
instructions::{Opcode, ValueList},
types::{I16, I32, I64, I8},
Block, DataFlowGraph, Function, Inst, InstBuilder, InstructionData, Type, Value, Block, DataFlowGraph, Function, Inst, InstBuilder, InstructionData, Type, Value,
}; };
use crate::isa::TargetIsa; use crate::isa::TargetIsa;
use crate::peepmatic::ValueOrInst;
use crate::timing; use crate::timing;
#[inline] #[inline]
@@ -183,12 +182,8 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
// U32 div by 1: identity // U32 div by 1: identity
// U32 rem by 1: zero // U32 rem by 1: zero
DivRemByConstInfo::DivU32(n1, 1) | DivRemByConstInfo::RemU32(n1, 1) => { DivRemByConstInfo::DivU32(_, 1) | DivRemByConstInfo::RemU32(_, 1) => {
if is_rem { unreachable!("unsigned division and remainder by one is handled in `preopt.peepmatic`");
pos.func.dfg.replace(inst).iconst(I32, 0);
} else {
replace_single_result_with_alias(&mut pos.func.dfg, inst, n1);
}
} }
// U32 div, rem by a power-of-2 // 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; let mask = (1u64 << k) - 1;
pos.func.dfg.replace(inst).band_imm(n1, mask as i64); pos.func.dfg.replace(inst).band_imm(n1, mask as i64);
} else { } 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 div by 1: identity
// U64 rem by 1: zero // U64 rem by 1: zero
DivRemByConstInfo::DivU64(n1, 1) | DivRemByConstInfo::RemU64(n1, 1) => { DivRemByConstInfo::DivU64(_, 1) | DivRemByConstInfo::RemU64(_, 1) => {
if is_rem { unreachable!("unsigned division and remainder by one is handled in `preopt.peepmatic`");
pos.func.dfg.replace(inst).iconst(I64, 0);
} else {
replace_single_result_with_alias(&mut pos.func.dfg, inst, n1);
}
} }
// U64 div, rem by a power-of-2 // 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; let mask = (1u64 << k) - 1;
pos.func.dfg.replace(inst).band_imm(n1, mask as i64); pos.func.dfg.replace(inst).band_imm(n1, mask as i64);
} else { } 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 div by 1: identity
// S32 rem by 1: zero // S32 rem by 1: zero
DivRemByConstInfo::DivS32(n1, 1) | DivRemByConstInfo::RemS32(n1, 1) => { DivRemByConstInfo::DivS32(_, 1) | DivRemByConstInfo::RemS32(_, 1) => {
if is_rem { unreachable!("signed division and remainder by one is handled in `preopt.peepmatic`");
pos.func.dfg.replace(inst).iconst(I32, 0);
} else {
replace_single_result_with_alias(&mut pos.func.dfg, inst, n1);
}
} }
DivRemByConstInfo::DivS32(n1, d) | DivRemByConstInfo::RemS32(n1, d) => { 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 div by 1: identity
// S64 rem by 1: zero // S64 rem by 1: zero
DivRemByConstInfo::DivS64(n1, 1) | DivRemByConstInfo::RemS64(n1, 1) => { DivRemByConstInfo::DivS64(_, 1) | DivRemByConstInfo::RemS64(_, 1) => {
if is_rem { unreachable!("division and remaineder by one are handled in `preopt.peepmatic`");
pos.func.dfg.replace(inst).iconst(I64, 0);
} else {
replace_single_result_with_alias(&mut pos.func.dfg, inst, n1);
}
} }
DivRemByConstInfo::DivS64(n1, d) | DivRemByConstInfo::RemS64(n1, d) => { 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<immediates::Imm64> {
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 { enum BranchOrderKind {
BrzToBrnz(Value), BrzToBrnz(Value),
BrnzToBrz(Value), BrnzToBrz(Value),
@@ -945,14 +599,20 @@ fn branch_order(pos: &mut FuncCursor, cfg: &mut ControlFlowGraph, block: Block,
} }
/// The main pre-opt pass. /// 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 _tt = timing::preopt();
let mut pos = FuncCursor::new(func); 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(block) = pos.next_block() {
while let Some(inst) = pos.next_inst() { while let Some(inst) = pos.next_inst() {
// Apply basic simplifications. preopt.apply_all(&mut pos, ValueOrInst::Inst(inst));
simplify(&mut pos, inst, native_word_width as u32); let inst = pos.current_inst().unwrap();
// Try to transform divide-by-constant into simpler operations. // Try to transform divide-by-constant into simpler operations.
if let Some(divrem_info) = get_div_info(inst, &pos.func.dfg) { 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; continue;
} }
branch_opt(&mut pos, inst);
branch_order(&mut pos, cfg, block, inst); branch_order(&mut pos, cfg, block, inst);
} }
} }

View File

@@ -6,9 +6,9 @@ function u0:0(i8) -> i8 fast {
block0(v0: i8): block0(v0: i8):
v1 = iconst.i8 0 v1 = iconst.i8 0
v2 = isub v1, v0 v2 = isub v1, v0
; check: v3 = uextend.i32 v0 ; check: v4 = uextend.i32 v0
; nextln: v5 = iconst.i32 0 ; nextln: v6 = iconst.i32 0
; nextln = isub v5, v3 ; nextln: v5 = isub v6, v4
; nextln = ireduce.i8 v4 ; nextln: v2 = ireduce.i8 v5
return v2 return v2
} }

View File

@@ -7,14 +7,13 @@ function %indir_udiv32(i32) -> i32 {
block0(v0: i32): block0(v0: i32):
v1 = iconst.i32 7 v1 = iconst.i32 7
v2 = udiv v0, v1 v2 = udiv v0, v1
; check: iconst.i32 7 ; check: v4 = iconst.i32 0x2492_4925
; check: iconst.i32 0x2492_4925 ; nextln: v5 = umulhi v0, v4
; check: umulhi v0, v3 ; nextln: v6 = isub v0, v5
; check: isub v0, v4 ; nextln: v7 = ushr_imm v6, 1
; check: ushr_imm v5, 1 ; nextln: v8 = iadd v7, v5
; check: iadd v6, v4 ; nextln: v9 = ushr_imm v8, 2
; check: v8 = ushr_imm v7, 2 ; nextln: v2 -> v9
; check: v2 -> v8
return v2 return v2
} }
@@ -22,13 +21,12 @@ function %indir_sdiv32(i32) -> i32 {
block0(v0: i32): block0(v0: i32):
v1 = iconst.i32 -17 v1 = iconst.i32 -17
v2 = sdiv v0, v1 v2 = sdiv v0, v1
; check: iconst.i32 -17 ; check: v4 = iconst.i32 0xffff_ffff_8787_8787
; check: iconst.i32 0xffff_ffff_8787_8787 ; nextln: v5 = smulhi v0, v4
; check: smulhi v0, v3 ; nextln: v6 = sshr_imm v5, 3
; check: sshr_imm v4, 3 ; nextln: v7 = ushr_imm v6, 31
; check: ushr_imm v5, 31 ; nextln: v8 = iadd v6, v7
; check: v7 = iadd v5, v6 ; nextln: v2 -> v8
; check: v2 -> v7
return v2 return v2
} }
@@ -36,11 +34,10 @@ function %indir_udiv64(i64) -> i64 {
block0(v0: i64): block0(v0: i64):
v1 = iconst.i64 1337 v1 = iconst.i64 1337
v2 = udiv v0, v1 v2 = udiv v0, v1
; check: iconst.i64 1337 ; check: v4 = iconst.i64 0xc411_9d95_2866_a139
; check: iconst.i64 0xc411_9d95_2866_a139 ; nextln: v5 = umulhi v0, v4
; check: umulhi v0, v3 ; nextln: v6 = ushr_imm v5, 10
; check: v5 = ushr_imm v4, 10 ; nextln: v2 -> v6
; check: v2 -> v5
return v2 return v2
} }
@@ -48,12 +45,11 @@ function %indir_sdiv64(i64) -> i64 {
block0(v0: i64): block0(v0: i64):
v1 = iconst.i64 -90210 v1 = iconst.i64 -90210
v2 = sdiv v0, v1 v2 = sdiv v0, v1
; check: iconst.i64 0xffff_ffff_fffe_9f9e ; check: v4 = iconst.i64 0xd181_4ee8_939c_b8bb
; check: iconst.i64 0xd181_4ee8_939c_b8bb ; nextln: v5 = smulhi v0, v4
; check: smulhi v0, v3 ; nextln: v6 = sshr_imm v5, 14
; check: sshr_imm v4, 14 ; nextln: v7 = ushr_imm v6, 63
; check: ushr_imm v5, 63 ; nextln: v8 = iadd v6, v7
; check: v7 = iadd v5, v6 ; nextln: v2 -> v8
; check: v2 -> v7
return v2 return v2
} }

View File

@@ -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
}

View File

@@ -58,4 +58,3 @@ block0(v0: i64):
; nextln: v2 = iadd v0, v1 ; nextln: v2 = iadd v0, v1
; nextln: return v2 ; nextln: return v2
; nextln: } ; nextln: }

View File

@@ -44,6 +44,37 @@ block0(v0: i32):
; nextln: return v3 ; nextln: return v3
; nextln: } ; 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) { function %brz_bint(i32) {
block0(v0: i32): block0(v0: i32):
v3 = icmp_imm slt v0, 0 v3 = icmp_imm slt v0, 0

View File

@@ -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
}

View File

@@ -38,6 +38,7 @@ impl SubTest for TestSimplePreopt {
.preopt(isa) .preopt(isa)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?; .map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
let text = &comp_ctx.func.display(isa).to_string(); let text = &comp_ctx.func.display(isa).to_string();
log::debug!("After simple_preopt:\n{}", text);
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -160,6 +160,6 @@ mod tests {
#[test] #[test]
fn compile_preopt() { fn compile_preopt() {
assert_compiles("examples/preopt.peepmatic"); assert_compiles("../codegen/src/preopt.peepmatic");
} }
} }