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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ docs/book
|
|||||||
rusty-tags.*
|
rusty-tags.*
|
||||||
tags
|
tags
|
||||||
target
|
target
|
||||||
|
.z3-trace
|
||||||
|
|||||||
294
Cargo.lock
generated
294
Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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" }
|
||||||
|
|||||||
@@ -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`");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
847
cranelift/codegen/src/peepmatic.rs
Normal file
847
cranelift/codegen/src/peepmatic.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.)
|
||||||
|
|
||||||
BIN
cranelift/codegen/src/preopt.serialized
Normal file
BIN
cranelift/codegen/src/preopt.serialized
Normal file
Binary file not shown.
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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: }
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user