diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aff0052eddc8..ad8e071cc785 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -523,7 +523,7 @@ jobs: --exclude wasmtime-wasi-tls \ --exclude wasmtime-fuzzing \ --exclude wasm-spec-interpreter \ - --exclude veri_engine \ + --exclude cranelift-isle-veri \ --exclude calculator \ --exclude wasi-preview1-component-adapter diff --git a/.gitignore b/.gitignore index d0136b3dcb42..475d2a6befda 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,6 @@ examples/.cache crates/c-api/build *.coredump *.smt2 -cranelift/isle/veri/veri_engine/test_output crates/explorer/node_modules *.cwasm /artifacts diff --git a/Cargo.lock b/Cargo.lock index d9585a148a8b..efe6e1405aa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,18 +13,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9698bf0769c641b18618039fe2ebd41eb3541f98433000f64e663fab7cea2c87" +checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a" dependencies = [ "gimli 0.33.0", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" @@ -34,14 +34,14 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures 0.2.7", + "cpufeatures 0.2.17", ] [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -57,9 +57,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "ambient-authority" @@ -75,9 +75,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -90,15 +90,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -161,7 +161,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -172,9 +172,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "backtrace" @@ -191,6 +191,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -199,9 +205,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "beef" @@ -215,7 +221,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -225,8 +231,8 @@ dependencies = [ "quote", "regex", "rustc-hash", - "shlex", - "syn 2.0.106", + "shlex 1.3.0", + "syn 2.0.117", ] [[package]] @@ -252,35 +258,35 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bstr" -version = "1.6.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", - "regex-automata 0.3.3", + "regex-automata", "serde", ] [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" dependencies = [ "allocator-api2", ] @@ -291,9 +297,9 @@ version = "47.0.0" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -303,9 +309,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "bytesize" -version = "2.0.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c8f83209414aacf0eeae3cf730b18d6981697fba62f200fcfb92b9f082acba" +checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" [[package]] name = "bzip2" @@ -319,12 +325,11 @@ dependencies = [ [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -339,11 +344,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.4" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -367,7 +372,7 @@ dependencies = [ "cap-primitives", "cap-std", "rustix 1.1.4", - "smallvec 1.15.1", + "smallvec", ] [[package]] @@ -421,9 +426,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.2" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -439,7 +444,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -450,14 +455,14 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.41" +version = "1.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -471,9 +476,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chacha20" @@ -483,14 +488,14 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -499,18 +504,18 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 1.8.2", + "half", ] [[package]] @@ -536,9 +541,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -546,9 +551,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -559,30 +564,30 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.58" +version = "4.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a" +checksum = "e0a7a9bfdb35811f9e59832f0f05975114d2251b415fb534108e6f34060fd772" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cobs" @@ -590,7 +595,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -600,21 +605,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", - "unicode-width 0.1.9", + "unicode-width 0.1.14", ] [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "component-async-tests" version = "0.0.0" dependencies = [ "bytes", - "env_logger 0.11.5", + "env_logger 0.11.10", "futures", "once_cell", "tempfile", @@ -650,26 +655,36 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpp_demangle" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" dependencies = [ "cfg-if", ] [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -755,9 +770,9 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "criterion", - "env_logger 0.11.5", + "env_logger 0.11.10", "gimli 0.33.0", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "libm", "log", "postcard", @@ -769,7 +784,7 @@ dependencies = [ "serde_derive", "sha2", "similar", - "smallvec 1.15.1", + "smallvec", "souper-ir", "target-lexicon", "wasmtime-internal-core", @@ -782,7 +797,7 @@ dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", "cranelift-srcgen", - "heck 0.5.0", + "heck", "pulley-interpreter", ] @@ -831,9 +846,9 @@ dependencies = [ "serde", "serde_derive", "similar", - "smallvec 1.15.1", + "smallvec", "target-lexicon", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml", "wasmtime-internal-unwinder", "wat", @@ -844,11 +859,11 @@ name = "cranelift-frontend" version = "0.134.0" dependencies = [ "cranelift-codegen", - "env_logger 0.11.5", - "hashbrown 0.17.0", + "env_logger 0.11.10", + "hashbrown 0.17.1", "log", "similar", - "smallvec 1.15.1", + "smallvec", "target-lexicon", ] @@ -874,8 +889,8 @@ dependencies = [ "cranelift-reader", "libm", "log", - "smallvec 1.15.1", - "thiserror 2.0.17", + "smallvec", + "thiserror 2.0.18", ] [[package]] @@ -886,6 +901,67 @@ dependencies = [ "log", ] +[[package]] +name = "cranelift-isle-veri" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "cranelift-codegen-meta", + "cranelift-isle", + "cranelift-isle-veri-test-macros", + "easy-smt", + "env_logger 0.11.10", + "log", + "num-bigint", + "num-traits", + "rayon", + "serde", + "serde_json", + "tempfile", +] + +[[package]] +name = "cranelift-isle-veri-aslp" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "cranelift-isle-veri-test-macros", + "enquote", + "pest", + "pest_derive", + "reqwest", + "serde", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "cranelift-isle-veri-isaspec" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "cranelift-codegen", + "cranelift-isle", + "cranelift-isle-veri-aslp", + "itertools 0.14.0", + "reqwest", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "cranelift-isle-veri-test-macros" +version = "0.1.0" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "cranelift-jit" version = "0.134.0" @@ -915,7 +991,7 @@ dependencies = [ "anyhow", "cranelift-codegen", "cranelift-control", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_derive", ] @@ -941,7 +1017,7 @@ dependencies = [ "cranelift-module", "gimli 0.33.0", "log", - "object 0.39.0", + "object 0.39.1", "target-lexicon", ] @@ -951,7 +1027,7 @@ version = "0.134.0" dependencies = [ "anyhow", "cranelift-codegen", - "smallvec 1.15.1", + "smallvec", "target-lexicon", ] @@ -988,7 +1064,7 @@ dependencies = [ "cranelift-native", "cranelift-object", "cranelift-reader", - "env_logger 0.11.5", + "env_logger 0.11.10", "filecheck", "log", "pulley-interpreter", @@ -998,16 +1074,16 @@ dependencies = [ "serde", "similar", "target-lexicon", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml", "walkdir", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1048,11 +1124,10 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] @@ -1068,21 +1143,21 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1103,16 +1178,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "pem-rfc7468", - "zeroize", -] - [[package]] name = "deranged" version = "0.5.8" @@ -1124,13 +1189,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d475dfebcb4854d596b17b09f477616f80f17a550517f2b3615d8c205d5c802b" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1167,29 +1232,31 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "dlmalloc" -version = "0.2.4" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" +checksum = "ad5208a115eaba24916f7456929832e310a81518c641f93fee4f89aa93aa3675" dependencies = [ + "cfg-if", "libc", + "windows-sys 0.61.2", ] [[package]] name = "easy-smt" -version = "0.2.2" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc74633db03a8b18de7c933fbd72402d45dfaf2d1736c1fd8ff9bbe461b4572" +checksum = "5f07ca2cd196ea6f5c1a728d609f4a38b02d5d0f1bd26c913929396bd38002d6" dependencies = [ "log", "unicode-segmentation", @@ -1201,19 +1268,19 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05a6c0bbc92278f84e742f08c0ab9cb16a987376cd2bc39d228ef9c74d98d6f7" dependencies = [ - "indexmap 1.9.1", + "indexmap 1.9.3", "instant", "log", "once_cell", - "smallvec 1.15.1", + "smallvec", "symbolic_expressions", ] [[package]] name = "either" -version = "1.13.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "embedded-io" @@ -1239,18 +1306,27 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror 1.0.69", +] + [[package]] name = "env_filter" -version = "0.1.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" dependencies = [ "log", "regex", @@ -1258,9 +1334,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -1271,22 +1347,22 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -1300,18 +1376,15 @@ dependencies = [ [[package]] name = "escape8259" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4911e3666fcd7826997b4745c8224295a6f3072f1418c3067b97a67557ee" -dependencies = [ - "rustversion", -] +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" [[package]] name = "example-component-wasm" version = "0.0.0" dependencies = [ - "wit-bindgen 0.57.0", + "wit-bindgen 0.57.1", ] [[package]] @@ -1322,7 +1395,7 @@ version = "0.0.0" name = "example-resource-component-wasm" version = "0.1.0" dependencies = [ - "wit-bindgen 0.57.0", + "wit-bindgen 0.57.1", ] [[package]] @@ -1350,9 +1423,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "file-per-thread-logger" @@ -1360,7 +1433,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a3cc21c33af89af0930c8cae4ade5e6fdc17b5d2c97b3d2e2edb67a1cf683f3" dependencies = [ - "env_logger 0.10.0", + "env_logger 0.10.2", "log", ] @@ -1371,26 +1444,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fe00b427b7c4835f8b82170eb7b9a63634376b63d73b9a9093367e82570bbaa" dependencies = [ "regex", - "thiserror 1.0.65", + "thiserror 1.0.69", ] [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "winapi", ] [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -1400,18 +1471,18 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flagset" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" dependencies = [ "serde", ] [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1425,9 +1496,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foldhash" @@ -1465,9 +1536,9 @@ version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" dependencies = [ - "io-lifetimes 2.0.3", + "io-lifetimes 2.0.4", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1482,9 +1553,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1497,9 +1568,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1507,15 +1578,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1524,9 +1595,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1545,32 +1616,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1580,7 +1651,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1590,7 +1660,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "debugid", "rustc-hash", "serde", @@ -1604,7 +1674,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bafc7e33650ab9f05dcc16325f05d56b8d10393114e31a19a353b86fa60cfe7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cfg-if", "log", "managed", @@ -1624,9 +1694,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1634,25 +1704,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "r-efi 5.3.0", + "wasip2", ] [[package]] @@ -1663,8 +1733,8 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", - "rand_core 0.10.0", + "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -1694,31 +1764,31 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gzip-header" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" +checksum = "86848f4fd157d91041a62c78046fb7b248bcc2dce78376d436a1756e9a038577" dependencies = [ "crc32fast", ] [[package]] name = "h2" -version = "0.4.13" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ - "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http", + "futures-util", + "http 0.2.12", "indexmap 2.14.0", "slab", "tokio", @@ -1727,19 +1797,33 @@ dependencies = [ ] [[package]] -name = "half" -version = "1.8.2" +name = "h2" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.1", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] name = "half" -version = "2.4.1" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -1750,11 +1834,11 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash 0.1.3", + "foldhash 0.1.5", ] [[package]] @@ -1762,27 +1846,18 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash 0.2.0", -] [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ "foldhash 0.2.0", "serde", "serde_core", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1804,6 +1879,12 @@ dependencies = [ "digest", ] +[[package]] +name = "hmac-sha256" +version = "1.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d92d097f4749b64e8cc33d924d9f40a2d4eb91402b458014b781f5733d60f" + [[package]] name = "home" version = "0.5.12" @@ -1815,15 +1896,36 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", "itoa", ] +[[package]] +name = "http" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1831,7 +1933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.1", ] [[package]] @@ -1842,8 +1944,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.4.1", + "http-body 1.0.1", "pin-project-lite", ] @@ -1855,55 +1957,94 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" -version = "1.9.0" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2", - "http", - "http-body", + "h2 0.4.14", + "http 1.4.1", + "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", - "smallvec 1.15.1", + "smallvec", "tokio", "want", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", + "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1913,98 +2054,60 @@ dependencies = [ ] [[package]] -name = "icu_locid_transform" -version = "1.5.0" +name = "icu_normalizer" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", + "icu_collections", + "icu_normalizer_data", + "icu_properties", "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec 1.15.1", - "utf16_iter", - "utf8_iter", - "write16", + "smallvec", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ - "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "id-arena" version = "2.3.0" @@ -2018,15 +2121,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", - "smallvec 1.15.1", + "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -2034,9 +2137,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -2049,16 +2152,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -2079,14 +2182,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20fd6de4ccfcc187e38bc21cfa543cb5a302cb86a8b114eb7f0bf0dc9f8ac00f" dependencies = [ "io-lifetimes 3.0.1", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "io-lifetimes" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" [[package]] name = "io-lifetimes" @@ -2096,9 +2199,9 @@ checksum = "2f0fb0570afe1fed943c5c3d4102d5358592d8625fda6a0007fdbe65a92fba96" [[package]] name = "ipnet" -version = "2.5.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "is-terminal" @@ -2113,16 +2216,16 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "isle-fuzz" version = "0.0.0" dependencies = [ "cranelift-isle", - "env_logger 0.11.5", + "env_logger 0.11.10", "libfuzzer-sys", "log", ] @@ -2133,7 +2236,7 @@ version = "0.0.0" dependencies = [ "clap", "cranelift-isle", - "env_logger 0.11.5", + "env_logger 0.11.10", ] [[package]] @@ -2156,9 +2259,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "ittapi" @@ -2180,15 +2283,52 @@ dependencies = [ "cc", ] +[[package]] +name = "jiff" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom 0.3.4", "libc", ] +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + [[package]] name = "json-from-wast" version = "0.251.0" @@ -2203,15 +2343,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +checksum = "6cc46bac87ef8093eed6f272babb833b6443374399985ac8ed28471ee0918545" [[package]] name = "leb128fmt" @@ -2221,15 +2361,15 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libfuzzer-sys" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" dependencies = [ "arbitrary", "cc", @@ -2237,12 +2377,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets", + "windows-link", ] [[package]] @@ -2251,11 +2391,20 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "libc", +] + [[package]] name = "libtest-mimic" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" +checksum = "14e6ba06f0ade6e504aff834d7c34298e5155c6baca353cc6a4aaff2f9fd7f33" dependencies = [ "anstream", "anstyle", @@ -2265,9 +2414,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" @@ -2277,49 +2426,55 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.7.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.28" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "logos" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6b6e02facda28ca5fb8dbe4b152496ba3b1bd5a4b40bb2b1b2d8ad74e0f39b" +checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" dependencies = [ "logos-derive", ] [[package]] name = "logos-codegen" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32eb6b5f26efacd015b000bfc562186472cd9b34bdba3f6b264e2a052676d10" +checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" dependencies = [ "beef", "fnv", "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.8.5", - "syn 2.0.106", + "regex-syntax", + "syn 2.0.117", ] [[package]] name = "logos-derive" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5d0c5463c911ef55624739fc353238b4e310f0144be1f875dc42fec6bfd5ec" +checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" dependencies = [ "logos-codegen", ] +[[package]] +name = "lzma-rust2" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e20f57f9918e5bd7bc58c22cdd70a6afc7375d4dd9683af5f2b34bd3d2bba619" + [[package]] name = "mach2" version = "0.4.3" @@ -2343,7 +2498,7 @@ checksum = "59a9dbbfc75d2688ed057456ce8a3ee3f48d12eec09229f560f3643b9f275653" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2358,14 +2513,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.4.11", + "regex-automata", ] [[package]] name = "matrixmultiply" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" dependencies = [ "autocfg", "rawpointer", @@ -2379,9 +2534,9 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "memfd" @@ -2401,12 +2556,18 @@ dependencies = [ "libc", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "min-platform-host" version = "47.0.0" dependencies = [ "libloading", - "object 0.39.0", + "object 0.39.1", "wasmtime", ] @@ -2428,20 +2589,20 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.61.2", ] [[package]] name = "mutatis" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645a17bdae66c8ee0fdba0004ab864cbd711bec41aae4c466ce0d35e266c946e" +checksum = "bce77d27895cb8993f1ad0d4418c1caaa1fb622dfa5e5955280ff3e5e1646f9e" dependencies = [ "log", "mutatis-derive", @@ -2450,20 +2611,20 @@ dependencies = [ [[package]] name = "mutatis-derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e2c58c651085c4319cd3554f44541dcf1be8daa0481c9089de50926750623e7" +checksum = "588b7a72dccbc8e1f3b30c9aa8be46ca5adc559eb1f040dda98c0a7bc1de3342" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -2508,6 +2669,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -2519,9 +2690,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -2562,12 +2733,12 @@ dependencies = [ [[package]] name = "object" -version = "0.39.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63944c133d03f44e75866bbd160b95af0ec3f6a13d936d69d31c81078cbc5baf" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ "crc32fast", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "indexmap 2.14.0", "memchr", ] @@ -2603,21 +2774,21 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "openssl" @@ -2625,7 +2796,7 @@ version = "0.10.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cfg-if", "foreign-types", "libc", @@ -2641,14 +2812,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" @@ -2664,9 +2835,9 @@ dependencies = [ [[package]] name = "openvino" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3308ec088481c27b5b521598ced2d5d5f67253d9639f5a3cce5a2ea4c4062a94" +checksum = "94957bde49dd820f68c962949115ba8d012c0fe3d604b7722de72b6bf3398724" dependencies = [ "openvino-finder", "openvino-sys", @@ -2674,9 +2845,9 @@ dependencies = [ [[package]] name = "openvino-finder" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bba5393f3522f98d9c4703a6a73afc7feff2bf9cc00a0722957b54c44ecda5fe" +checksum = "cf255f8d09ab4ab7f6fd08326fa5df9f875b57807ee8eb1702ce1a88db7d0390" dependencies = [ "cfg-if", "log", @@ -2684,36 +2855,34 @@ dependencies = [ [[package]] name = "openvino-sys" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7d035914ff5c8e12d7e05982929fb275e6f06eafcbe529f316001760b08786" +checksum = "1ac8d4a70a99d5afb7091e6c0b7c2b2bb509b6b3322877ccb97d2075553f9c01" dependencies = [ - "env_logger 0.11.5", + "env_logger 0.11.10", "libloading", "openvino-finder", ] [[package]] name = "ort" -version = "2.0.0-rc.10" +version = "2.0.0-rc.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa7e49bd669d32d7bc2a15ec540a527e7764aec722a45467814005725bcd721" +checksum = "d7de3af33d24a745ffb8fab904b13478438d1cd52868e6f17735ef6e1f8bf133" dependencies = [ "ort-sys", - "smallvec 2.0.0-alpha.10", + "smallvec", "tracing", ] [[package]] name = "ort-sys" -version = "2.0.0-rc.10" +version = "2.0.0-rc.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2aba9f5c7c479925205799216e7e5d07cc1d4fa76ea8058c60a9a30f6a4e890" +checksum = "d7b497d21a8b6fbb4b5a544f8fadb77e801a09ae0add9e411d31c6f89e3c1e90" dependencies = [ - "flate2", - "pkg-config", - "sha2", - "tar", + "hmac-sha256", + "lzma-rust2", "ureq", ] @@ -2740,21 +2909,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] [[package]] name = "paste" -version = "1.0.7" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pastey" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" [[package]] name = "pbkdf2" @@ -2769,19 +2938,53 @@ dependencies = [ ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ - "base64ct", + "pest", + "pest_generator", ] [[package]] -name = "percent-encoding" -version = "2.3.2" +name = "pest_generator" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] [[package]] name = "petgraph" @@ -2795,21 +2998,30 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pkg-config" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] -name = "pkg-config" -version = "0.3.32" +name = "portable-atomic" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] [[package]] name = "postcard" @@ -2823,6 +3035,15 @@ dependencies = [ "serde", ] +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2831,25 +3052,52 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" -version = "0.2.31" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -2862,12 +3110,12 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.11.1", + "bitflags 2.12.1", "num-traits", "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", - "regex-syntax 0.8.5", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -2881,7 +3129,7 @@ dependencies = [ "arbitrary", "clap", "cranelift-bitset", - "env_logger 0.11.5", + "env_logger 0.11.10", "log", "pulley-macros", "termcolor", @@ -2892,7 +3140,7 @@ dependencies = [ name = "pulley-interpreter-fuzz" version = "0.0.0" dependencies = [ - "env_logger 0.11.5", + "env_logger 0.11.10", "log", "pulley-interpreter", ] @@ -2903,7 +3151,7 @@ version = "47.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2914,13 +3162,19 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.41" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -2935,7 +3189,7 @@ checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -2945,7 +3199,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2956,7 +3210,7 @@ checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", "getrandom 0.4.2", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -2966,7 +3220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -2976,32 +3230,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.4", ] [[package]] name = "rand_core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_xorshift" @@ -3009,16 +3263,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] name = "raw-cpuid" -version = "11.5.0" +version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -3029,44 +3283,33 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.5.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", - "redox_syscall", - "thiserror 1.0.65", + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", ] [[package]] @@ -3077,45 +3320,34 @@ checksum = "de2c52737737f8609e94f975dee22854a2d5c125772d4b1cf292120f4d45c186" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "log", "rustc-hash", "serde", - "smallvec 1.15.1", + "smallvec", ] [[package]] name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.3.3", - "regex-syntax 0.7.4", -] - -[[package]] -name = "regex-automata" -version = "0.3.3" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] @@ -3127,15 +3359,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "region" @@ -3149,6 +3375,46 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.17.14" @@ -3157,7 +3423,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -3165,27 +3431,27 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "errno", "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", ] [[package]] @@ -3194,7 +3460,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -3213,9 +3479,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "log", "once_cell", @@ -3226,11 +3492,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "zeroize", ] @@ -3248,15 +3523,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -3266,9 +3541,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "safetensors" @@ -3291,21 +3566,21 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.1", - "core-foundation", + "bitflags 2.12.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3313,9 +3588,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -3323,9 +3598,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -3358,30 +3633,43 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -3402,26 +3690,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures 0.2.7", + "cpufeatures 0.2.17", "digest", ] [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures 0.2.7", + "cpufeatures 0.2.17", "digest", ] [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -3432,6 +3720,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "shuffling-allocator" version = "1.1.2" @@ -3446,18 +3740,19 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "similar" @@ -3467,9 +3762,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -3481,16 +3776,20 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "2.0.0-alpha.10" +name = "socket2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d44cfb396c3caf6fbfd0ab422af02631b69ddd96d2eff0b0f0724f9024051b" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -3524,9 +3823,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -3540,7 +3839,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23de088478b31c349c9ba67816fa55d9355232d63c3afea8bf513e31f0f1d2c0" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.5", "serde", ] @@ -3550,30 +3849,11 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.92", -] - [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "symbolic_expressions" @@ -3583,46 +3863,61 @@ checksum = "7c68d531d83ec6c531150584c42a4290911964d5f0d79132b193b67252a23b71" [[package]] name = "syn" -version = "1.0.92" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "syn" -version = "2.0.106" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] -name = "tar" -version = "0.4.46" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "filetime", + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", "libc", - "xattr", ] [[package]] @@ -3637,13 +3932,13 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3585f5bbf1ddf2498d7586bf870c7bb785a0bf1be09c54d0f93fce51d5f3c7fc" dependencies = [ - "half 2.4.1", + "half", "lazy_static", "libc", "ndarray", "rand 0.8.6", "safetensors", - "thiserror 1.0.65", + "thiserror 1.0.69", "torch-sys", "zip", ] @@ -3654,7 +3949,7 @@ version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.3.0", + "fastrand 2.4.1", "getrandom 0.4.2", "once_cell", "rustix 1.1.4", @@ -3682,23 +3977,33 @@ dependencies = [ [[package]] name = "test-log" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e33b98a582ea0be1168eba097538ee8dd4bbe0f2b01b22ac92ea30054e5be7b" +checksum = "9b9c218384242b5c89b68303ab6f6fc53a312d923f0c14dc6bb860c6aeee40f1" dependencies = [ "test-log-macros", "tracing-subscriber", ] [[package]] -name = "test-log-macros" -version = "0.2.18" +name = "test-log-core" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36" +checksum = "c26ef8b00e4d382e59f6a8ddb3cd790b3a5bb29f21a358a9a69ea2f29f13f27b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", +] + +[[package]] +name = "test-log-macros" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944ad38adcbb71eaa682c56bceeb079e4ca82b4b3edc2a0fde5cb297b77dac8d" +dependencies = [ + "syn 2.0.117", + "test-log-core", ] [[package]] @@ -3706,7 +4011,7 @@ name = "test-programs" version = "0.0.0" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "flate2", "futures", "libc", @@ -3717,7 +4022,7 @@ dependencies = [ "wasi-nn", "wasip1", "wasip2", - "wit-bindgen 0.57.0", + "wit-bindgen 0.57.1", ] [[package]] @@ -3725,7 +4030,7 @@ name = "test-programs-artifacts" version = "0.0.0" dependencies = [ "cargo_metadata", - "heck 0.5.0", + "heck", "rayon", "serde", "serde_derive", @@ -3737,51 +4042,51 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.65", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "once_cell", + "cfg-if", ] [[package]] @@ -3805,9 +4110,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -3825,16 +4130,16 @@ dependencies = [ [[package]] name = "tokio" -version = "1.51.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.4", "tokio-macros", "windows-sys 0.61.2", ] @@ -3847,7 +4152,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -3883,9 +4188,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -3896,9 +4201,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap 2.14.0", "serde_core", @@ -3906,32 +4211,32 @@ dependencies = [ "toml_datetime", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "torch-sys" @@ -3945,11 +4250,17 @@ dependencies = [ "zip", ] +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -3959,20 +4270,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -3991,14 +4302,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex-automata 0.4.11", + "regex-automata", "sharded-slab", "thread_local", "tracing", @@ -4008,15 +4319,15 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.15.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uap-bench" @@ -4027,6 +4338,12 @@ dependencies = [ "serde_yaml", ] +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unarray" version = "0.1.4" @@ -4035,33 +4352,33 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unsafe-libyaml" @@ -4077,39 +4394,35 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.1.4" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ - "base64", - "der", + "base64 0.22.1", "log", - "native-tls", "percent-encoding", - "rustls-pki-types", "socks", "ureq-proto", - "utf-8", - "webpki-root-certs", + "utf8-zero", ] [[package]] name = "ureq-proto" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ - "base64", - "http", + "base64 0.22.1", + "http 1.4.1", "httparse", "log", ] [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -4118,16 +4431,10 @@ dependencies = [ ] [[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf16_iter" -version = "1.0.5" +name = "utf8-zero" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" [[package]] name = "utf8_iter" @@ -4143,9 +4450,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.0.0" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "v8" @@ -4154,7 +4465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33ccf9cc537275d7bb848e94f160986164e2ea5138e60166551d115c7ea8f1a" dependencies = [ "bindgen", - "bitflags 2.11.1", + "bitflags 2.12.1", "fslock", "gzip-header", "home", @@ -4165,9 +4476,9 @@ dependencies = [ [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -4175,28 +4486,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "veri_engine" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "cranelift-codegen", - "cranelift-codegen-meta", - "cranelift-isle", - "easy-smt", - "env_logger 0.11.5", - "itertools 0.14.0", - "log", - "strum", - "strum_macros", - "veri_ir", -] - -[[package]] -name = "veri_ir" -version = "0.1.0" - [[package]] name = "verify-component-adapter" version = "47.0.0" @@ -4208,15 +4497,15 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -4239,28 +4528,18 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" -dependencies = [ - "wit-bindgen-rt", -] +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi-nn" @@ -4268,19 +4547,19 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7031683cc05a71515d9200fb159b28d717ded3c40dbb979d1602cf46f3a68f40" dependencies = [ - "thiserror 1.0.65", + "thiserror 1.0.69", ] [[package]] name = "wasi-preview1-component-adapter" version = "47.0.0" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "byte-array-literals", - "object 0.39.0", + "object 0.39.1", "wasip1", "wasm-encoder 0.251.0", - "wit-bindgen-rust-macro 0.57.0", + "wit-bindgen-rust-macro 0.57.1", ] [[package]] @@ -4291,11 +4570,11 @@ checksum = "b5e26842486624357dbeb8f0381cf1fb42f022291fd787d4a816768fec8cc760" [[package]] name = "wasip2" -version = "1.0.0+wasi-0.2.4" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen 0.45.1", + "wit-bindgen 0.57.1", ] [[package]] @@ -4307,6 +4586,61 @@ dependencies = [ "wit-bindgen 0.51.0", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + [[package]] name = "wasm-compose" version = "0.251.0" @@ -4314,11 +4648,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b089037d7eb453ed57b560fe7833de0707411c8b9fdc429745ced77e2a1bacb9" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "indexmap 2.14.0", "log", "petgraph", - "smallvec 1.15.1", + "smallvec", "wasm-encoder 0.251.0", "wasmparser 0.251.0", "wat", @@ -4399,7 +4733,7 @@ dependencies = [ "egg", "log", "rand 0.10.1", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasm-encoder 0.251.0", "wasmparser 0.251.0", ] @@ -4435,15 +4769,15 @@ checksum = "49a25d67bb8d8b9913f2fad9f31b2354b43f4cf23bbd6913e21734ed5170e9a9" dependencies = [ "anyhow", "logos", - "thiserror 2.0.17", + "thiserror 2.0.18", "wit-parser 0.251.0", ] [[package]] name = "wasmi" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fda10642ea9ba8141d50976b2b3a02d30511baa4b938383b9fa823b35d4138" +checksum = "22bf475363d09d960b48275c4ea9403051add498a9d80c64dbc91edabab9d1d0" dependencies = [ "spin", "wasmi_collections", @@ -4454,27 +4788,27 @@ dependencies = [ [[package]] name = "wasmi_collections" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67707a7d9eaf65181751b39cf46a003acc2ae1fef7917add38c3ced4b2eb868" +checksum = "85851acbdffd675a9b699b3590406a1d37fc1e1fd073743c7c9cf47c59caacba" dependencies = [ "string-interner", ] [[package]] name = "wasmi_core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9ad2a338c53920b3fd427c2122219fd315dc09044dbda58606259d7854cbed" +checksum = "ef64cf60195d1f937dbaed592a5afce3e6d86868fb8070c5255bc41539d68f9d" dependencies = [ "libm", ] [[package]] name = "wasmi_ir" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a6079b0597d3793aedbd2a2c0eea595476acabd797df9bba9270fbe319fad0" +checksum = "5dcb572ce4400e06b5475819f3d6b9048513efbca785f0b9ef3a41747f944fd8" dependencies = [ "wasmi_core", ] @@ -4485,7 +4819,7 @@ version = "0.228.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "indexmap 2.14.0", ] @@ -4495,8 +4829,8 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.1", - "hashbrown 0.15.2", + "bitflags 2.12.1", + "hashbrown 0.15.5", "indexmap 2.14.0", "semver", ] @@ -4507,8 +4841,8 @@ version = "0.247.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80" dependencies = [ - "bitflags 2.11.1", - "hashbrown 0.17.0", + "bitflags 2.12.1", + "hashbrown 0.17.1", "indexmap 2.14.0", "semver", ] @@ -4519,8 +4853,8 @@ version = "0.251.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "437970b35b1a85cfde9c74b2398352d8d653f3bd8e3a3db0c063ea8f5b4b36ff" dependencies = [ - "bitflags 2.11.1", - "hashbrown 0.17.0", + "bitflags 2.12.1", + "hashbrown 0.17.1", "indexmap 2.14.0", "semver", "serde", @@ -4541,16 +4875,16 @@ dependencies = [ name = "wasmtime" version = "47.0.0" dependencies = [ - "addr2line 0.26.0", + "addr2line 0.26.1", "async-trait", - "bitflags 2.11.1", + "bitflags 2.12.1", "bumpalo", "bytes", "cc", "cfg-if", "cranelift-native", "encoding_rs", - "env_logger 0.11.5", + "env_logger 0.11.10", "futures", "fxprof-processed-profile", "gimli 0.33.0", @@ -4560,7 +4894,7 @@ dependencies = [ "log", "mach2 0.6.0", "memfd", - "object 0.39.0", + "object 0.39.1", "once_cell", "postcard", "proptest", @@ -4572,7 +4906,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "smallvec 1.15.1", + "smallvec", "target-lexicon", "tempfile", "tokio", @@ -4628,7 +4962,7 @@ dependencies = [ "async-trait", "bytes", "cap-std", - "env_logger 0.11.5", + "env_logger 0.11.10", "futures", "log", "tokio", @@ -4659,19 +4993,19 @@ dependencies = [ "cranelift-native", "cranelift-reader", "criterion", - "env_logger 0.11.5", + "env_logger 0.11.10", "filecheck", "futures", "gimli 0.33.0", - "http", + "http 1.4.1", "http-body-util", - "hyper", + "hyper 1.10.1", "libc", "libtest-mimic", "log", "memchr", "num_cpus", - "object 0.39.0", + "object 0.39.1", "pin-project-lite", "pulley-interpreter", "rand 0.10.1", @@ -4681,7 +5015,7 @@ dependencies = [ "serde_derive", "serde_json", "similar", - "smallvec 1.15.1", + "smallvec", "target-lexicon", "tempfile", "termcolor", @@ -4745,19 +5079,19 @@ dependencies = [ "cranelift-bforest", "cranelift-bitset", "cranelift-entity", - "env_logger 0.11.5", + "env_logger 0.11.10", "gimli 0.33.0", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "indexmap 2.14.0", "log", - "object 0.39.0", + "object 0.39.1", "postcard", "rustc-demangle", "semver", "serde", "serde_derive", "sha2", - "smallvec 1.15.1", + "smallvec", "target-lexicon", "wasm-encoder 0.251.0", "wasmparser 0.251.0", @@ -4772,7 +5106,7 @@ name = "wasmtime-environ-fuzz" version = "0.0.0" dependencies = [ "arbitrary", - "env_logger 0.11.5", + "env_logger 0.11.10", "libfuzzer-sys", "wasmparser 0.251.0", "wasmprinter", @@ -4795,7 +5129,7 @@ dependencies = [ "cranelift-interpreter", "cranelift-native", "cranelift-reader", - "env_logger 0.11.5", + "env_logger 0.11.10", "libfuzzer-sys", "log", "mutatis", @@ -4804,7 +5138,7 @@ dependencies = [ "pulley-interpreter-fuzz", "quote", "rand 0.10.1", - "smallvec 1.15.1", + "smallvec", "target-lexicon", "wasmparser 0.251.0", "wasmtime", @@ -4821,7 +5155,7 @@ dependencies = [ "backtrace", "cranelift-bforest", "cranelift-bitset", - "env_logger 0.11.5", + "env_logger 0.11.10", "futures", "log", "mutatis", @@ -4829,7 +5163,7 @@ dependencies = [ "rayon", "serde", "serde_json", - "smallvec 1.15.1", + "smallvec", "target-lexicon", "tempfile", "tokio", @@ -4862,9 +5196,9 @@ dependencies = [ name = "wasmtime-internal-cache" version = "47.0.0" dependencies = [ - "base64", + "base64 0.22.1", "directories-next", - "env_logger 0.11.5", + "env_logger 0.11.10", "filetime", "log", "postcard", @@ -4876,7 +5210,7 @@ dependencies = [ "toml", "wasmtime-environ", "windows-sys 0.61.2", - "zstd 0.13.0", + "zstd 0.13.3", ] [[package]] @@ -4891,7 +5225,7 @@ dependencies = [ "serde", "serde_json", "similar", - "syn 2.0.106", + "syn 2.0.117", "tracing", "wasmtime", "wasmtime-internal-component-util", @@ -4908,7 +5242,7 @@ name = "wasmtime-internal-core" version = "47.0.0" dependencies = [ "anyhow", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "libm", "serde", ] @@ -4926,11 +5260,11 @@ dependencies = [ "gimli 0.33.0", "itertools 0.14.0", "log", - "object 0.39.0", + "object 0.39.1", "pulley-interpreter", - "smallvec 1.15.1", + "smallvec", "target-lexicon", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasmparser 0.251.0", "wasmtime-environ", "wasmtime-internal-core", @@ -4943,7 +5277,7 @@ name = "wasmtime-internal-debugger" version = "47.0.0" dependencies = [ "async-trait", - "env_logger 0.11.5", + "env_logger 0.11.10", "log", "tokio", "wasmtime", @@ -4985,13 +5319,13 @@ version = "47.0.0" dependencies = [ "anyhow", "clap", - "env_logger 0.11.5", + "env_logger 0.11.10", "futures", "gdbstub", "gdbstub_arch", "log", "wasip2", - "wit-bindgen 0.57.0", + "wit-bindgen 0.57.1", "wstd", ] @@ -5004,7 +5338,7 @@ name = "wasmtime-internal-jit-debug" version = "47.0.0" dependencies = [ "cc", - "object 0.39.0", + "object 0.39.1", "rustix 1.1.4", "wasmtime-internal-versioned-export-macros", ] @@ -5026,7 +5360,7 @@ dependencies = [ "cfg-if", "cranelift-codegen", "log", - "object 0.39.0", + "object 0.39.1", "wasmtime-environ", ] @@ -5036,7 +5370,7 @@ version = "47.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -5046,7 +5380,7 @@ dependencies = [ "cranelift-codegen", "gimli 0.33.0", "log", - "object 0.39.0", + "object 0.39.1", "target-lexicon", "wasmparser 0.251.0", "wasmtime-environ", @@ -5059,8 +5393,8 @@ name = "wasmtime-internal-wit-bindgen" version = "47.0.0" dependencies = [ "anyhow", - "bitflags 2.11.1", - "heck 0.5.0", + "bitflags 2.12.1", + "heck", "indexmap 2.14.0", "wit-parser 0.251.0", ] @@ -5076,7 +5410,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wasmtime-test-util", ] @@ -5086,7 +5420,7 @@ version = "47.0.0" dependencies = [ "arbitrary", "arbtest", - "env_logger 0.11.5", + "env_logger 0.11.10", "indexmap 2.14.0", "proc-macro2", "quote", @@ -5108,13 +5442,13 @@ name = "wasmtime-wasi" version = "47.0.0" dependencies = [ "async-trait", - "bitflags 2.11.1", + "bitflags 2.12.1", "bytes", "cap-fs-ext", "cap-net-ext", "cap-std", "cfg-if", - "env_logger 0.11.5", + "env_logger 0.11.10", "futures", "io-lifetimes 3.0.1", "rand 0.10.1", @@ -5122,7 +5456,7 @@ dependencies = [ "tempfile", "test-log", "test-programs-artifacts", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "tracing-subscriber", @@ -5149,15 +5483,15 @@ name = "wasmtime-wasi-http" version = "47.0.0" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", - "env_logger 0.11.5", + "env_logger 0.11.10", "flate2", "futures", - "http", - "http-body", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.10.1", "rustls", "sha2", "tempfile", @@ -5173,7 +5507,7 @@ dependencies = [ "wasmtime-wasi", "wasmtime-wasi-http", "wasmtime-wasi-io", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -5207,7 +5541,7 @@ dependencies = [ "ort", "tch", "test-programs-artifacts", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "tracing-subscriber", "walkdir", @@ -5235,7 +5569,7 @@ dependencies = [ "tracing", "wasmtime", "wasmtime-wasi", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -5244,7 +5578,7 @@ version = "47.0.0" dependencies = [ "json-from-wast", "log", - "object 0.39.0", + "object 0.39.1", "serde_json", "tokio", "wasmparser 0.251.0", @@ -5258,7 +5592,7 @@ version = "47.0.0" dependencies = [ "clap", "criterion", - "env_logger 0.11.5", + "env_logger 0.11.10", "log", "rayon", "test-programs-artifacts", @@ -5290,7 +5624,7 @@ dependencies = [ "gimli 0.32.3", "leb128fmt", "memchr", - "unicode-width 0.2.0", + "unicode-width 0.2.2", "wasm-encoder 0.251.0", ] @@ -5304,19 +5638,29 @@ dependencies = [ ] [[package]] -name = "webpki-root-certs" -version = "1.0.4" +name = "web-sys" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ - "rustls-pki-types", + "js-sys", + "wasm-bindgen", ] [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -5329,7 +5673,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", - "rustix 0.38.43", + "rustix 0.38.44", "winsafe", ] @@ -5337,9 +5681,9 @@ dependencies = [ name = "wiggle" version = "47.0.0" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "proptest", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "wasmtime", @@ -5353,10 +5697,10 @@ dependencies = [ name = "wiggle-generate" version = "47.0.0" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wasmtime-environ", "witx", ] @@ -5367,7 +5711,7 @@ version = "47.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wiggle", "wiggle-generate", ] @@ -5376,9 +5720,9 @@ dependencies = [ name = "wiggle-test" version = "0.0.0" dependencies = [ - "env_logger 0.11.5", + "env_logger 0.11.10", "proptest", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "tracing-subscriber", "wasmtime", @@ -5403,11 +5747,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -5424,9 +5768,9 @@ dependencies = [ "cranelift-codegen", "gimli 0.33.0", "regalloc2", - "smallvec 1.15.1", + "smallvec", "target-lexicon", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasmparser 0.251.0", "wasmtime-environ", "wasmtime-internal-core", @@ -5442,7 +5786,7 @@ dependencies = [ "windows-core", "windows-implement", "windows-interface", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -5451,7 +5795,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -5462,7 +5806,7 @@ checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -5473,7 +5817,7 @@ checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -5482,13 +5826,40 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -5500,75 +5871,213 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + [[package]] name = "winnow" -version = "0.7.13" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + +[[package]] +name = "winreg" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] [[package]] name = "winsafe" @@ -5582,17 +6091,8 @@ version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ - "bitflags 2.11.1", - "windows-sys 0.52.0", -] - -[[package]] -name = "wit-bindgen" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" -dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", + "windows-sys 0.59.0", ] [[package]] @@ -5606,13 +6106,13 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.57.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5be608857dbf30f78c4c543b8e4fc5a3fbc1efb44852a04fb6e0844f6d0d8b8" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "futures", - "wit-bindgen-rust-macro 0.57.0", + "wit-bindgen-rust-macro 0.57.1", ] [[package]] @@ -5622,30 +6122,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "wit-parser 0.244.0", ] [[package]] name = "wit-bindgen-core" -version = "0.57.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf43313f2252c2df0757a675825a6afb8bfaae8487a720f0a728d4a042f2b1e" +checksum = "02dee27a2dc20d1008016c742ec9fc6ea498492994ba3750be7454cbc97ff04c" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "wit-parser 0.247.0", ] -[[package]] -name = "wit-bindgen-rt" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" -dependencies = [ - "bitflags 2.11.1", -] - [[package]] name = "wit-bindgen-rust" version = "0.51.0" @@ -5653,10 +6144,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "indexmap 2.14.0", "prettyplease", - "syn 2.0.106", + "syn 2.0.117", "wasm-metadata 0.244.0", "wit-bindgen-core 0.51.0", "wit-component 0.244.0", @@ -5664,17 +6155,17 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.57.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d627c556b10cb6d6575bfad59512eab613c5dd4ee387da055b3393fffe71c7a" +checksum = "b5007dae772945b7a5003d69d90a3a4a78929d41f19d004e980c4259a6af4484" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "indexmap 2.14.0", "prettyplease", - "syn 2.0.106", + "syn 2.0.117", "wasm-metadata 0.247.0", - "wit-bindgen-core 0.57.0", + "wit-bindgen-core 0.57.1", "wit-component 0.247.0", ] @@ -5688,25 +6179,25 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wit-bindgen-core 0.51.0", "wit-bindgen-rust 0.51.0", ] [[package]] name = "wit-bindgen-rust-macro" -version = "0.57.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe4b30298dcc5dbde153f1af8d0e45da7862b5f35c58da9cce65fbde5c6bffb" +checksum = "af9237d678e3513ad24e96fe98beacdc0db6405284ba2a2400418cf0d42caa89" dependencies = [ "anyhow", "macro-string", "prettyplease", "proc-macro2", "quote", - "syn 2.0.106", - "wit-bindgen-core 0.57.0", - "wit-bindgen-rust 0.57.0", + "syn 2.0.117", + "wit-bindgen-core 0.57.1", + "wit-bindgen-rust 0.57.1", ] [[package]] @@ -5716,7 +6207,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags 2.12.1", "indexmap 2.14.0", "log", "serde", @@ -5735,7 +6226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d567162a6b9843080e5e0053f696623ff694bae8ae017c9ec536d1873bbe3d8" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags 2.12.1", "indexmap 2.14.0", "log", "serde", @@ -5754,7 +6245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a5e60173c413659c689f0581b0cf5d1a2404077568f9ffdce748a9eb2fc913" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags 2.12.1", "indexmap 2.14.0", "log", "serde", @@ -5791,7 +6282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ffe4064318cdf3c08cb99343b44c039fcefe61ccdf58aa9975285f13d74d1fc" dependencies = [ "anyhow", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "id-arena", "indexmap 2.14.0", "log", @@ -5810,7 +6301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e960732e824fab95099971a09e638979347c94ca48568d3c854c945729196947" dependencies = [ "anyhow", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "id-arena", "indexmap 2.14.0", "log", @@ -5830,7 +6321,7 @@ checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" dependencies = [ "anyhow", "log", - "thiserror 1.0.65", + "thiserror 1.0.69", "wast 35.0.2", ] @@ -5838,7 +6329,7 @@ dependencies = [ name = "wizer-fuzz" version = "0.0.0" dependencies = [ - "env_logger 0.11.5", + "env_logger 0.11.10", "libfuzzer-sys", "log", "wasm-smith", @@ -5847,17 +6338,11 @@ dependencies = [ "wasmtime-wizer", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wstd" @@ -5869,8 +6354,8 @@ dependencies = [ "async-task", "bytes", "futures-lite", - "http", - "http-body", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", "itoa", "pin-project-lite", @@ -5888,26 +6373,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6a9df01a7fb39fbe7e9b5ef76f586f06425dd6f2be350de4781936f72f9899d" dependencies = [ "quote", - "syn 2.0.106", -] - -[[package]] -name = "xattr" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" -dependencies = [ - "libc", - "rustix 1.1.4", + "syn 2.0.117", ] [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5915,34 +6389,54 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "synstructure", ] @@ -5952,11 +6446,22 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -5965,13 +6470,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -5994,6 +6499,12 @@ dependencies = [ "zstd 0.11.2+zstd.1.5.2", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" @@ -6005,11 +6516,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ - "zstd-safe 7.0.0", + "zstd-safe 7.2.4", ] [[package]] @@ -6024,18 +6535,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.0.0" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 53e0a9a4e386..3234732a3614 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,8 +169,10 @@ members = [ "cranelift/assembler-x64/fuzz", "cranelift/isle/fuzz", "cranelift/isle/islec", - "cranelift/isle/veri/veri_engine", - "cranelift/isle/veri/veri_ir", + "cranelift/isle/veri/aslp", + "cranelift/isle/veri/isaspec", + "cranelift/isle/veri/test-macros", + "cranelift/isle/veri/veri", "cranelift/serde", "crates/bench-api", "crates/c-api/artifact", @@ -458,6 +460,14 @@ pin-project-lite = "0.2.14" sha2 = { version = "0.10.2", default-features = false } gdbstub = "0.7.10" gdbstub_arch = "0.3.3" +reqwest = "0.11" +pest = "2.7.8" +pest_derive = "2.7.8" +enquote = "1.1.0" +proc-macro-error = "1.0" +num-bigint = "0.4" +num-traits = "0.2" +easy-smt = "0.2.4" # ============================================================================= # diff --git a/ci/run-tests.py b/ci/run-tests.py index f273eb40f82f..69828fbd5401 100755 --- a/ci/run-tests.py +++ b/ci/run-tests.py @@ -18,7 +18,7 @@ # # - calculator (under examples/wasip2-plugins): an example that's tested separately. # -# - veri_engine: requires an SMT solver (z3) +# - cranelift-isle-veri. requires an SMT solver (z3 or cvc5) import subprocess import sys @@ -29,8 +29,8 @@ args.append('--exclude=wasmtime-wasi-tls') args.append('--exclude=wasmtime-fuzzing') args.append('--exclude=wasm-spec-interpreter') -args.append('--exclude=veri_engine') args.append('--exclude=calculator') +args.append('--exclude=cranelift-isle-veri') args.extend(sys.argv[1:]) result = subprocess.run(args) diff --git a/cranelift/codegen/build.rs b/cranelift/codegen/build.rs index 82b4e7a8e4d6..0e73a9f6d107 100644 --- a/cranelift/codegen/build.rs +++ b/cranelift/codegen/build.rs @@ -161,14 +161,14 @@ fn build_isle( let mut had_error = false; for compilation in &isle_compilations.items { - for file in &compilation.inputs { + for file in &compilation.tracked_inputs { println!("cargo:rerun-if-changed={}", file.display()); } if let Err(e) = run_compilation(compilation) { had_error = true; eprintln!("Error building ISLE files:"); - eprintln!("{e:?}"); + eprintln!("{e}"); #[cfg(not(feature = "isle-errors"))] { eprintln!("To see a more detailed error report, run: "); @@ -199,9 +199,8 @@ fn run_compilation(compilation: &IsleCompilation) -> Result<(), Errors> { let code = { let file_paths = compilation - .inputs - .iter() - .chain(compilation.untracked_inputs.iter()); + .paths() + .map_err(|e| Errors::from_io(e, "list isle compilation file paths"))?; let mut options = isle::codegen::CodegenOptions::default(); // Because we include!() the generated ISLE source, we cannot diff --git a/cranelift/codegen/meta/src/gen_isle.rs b/cranelift/codegen/meta/src/gen_isle.rs index 715d19974e44..13cc93838717 100644 --- a/cranelift/codegen/meta/src/gen_isle.rs +++ b/cranelift/codegen/meta/src/gen_isle.rs @@ -341,19 +341,6 @@ fn gen_common_isle( .unwrap(); } - // Immediates. - let imm_operands: Vec<_> = inst - .operands_in - .iter() - .filter(|o| { - !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block() - }) - .collect(); - assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),); - for op in imm_operands { - write!(&mut s, " {}", op.name).unwrap(); - } - // Blocks. let block_operands: Vec<_> = inst .operands_in @@ -387,6 +374,19 @@ fn gen_common_isle( _ => panic!("Too many raw block arguments"), } + // Immediates. + let imm_operands: Vec<_> = inst + .operands_in + .iter() + .filter(|o| { + !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block() + }) + .collect(); + assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),); + for op in imm_operands { + write!(&mut s, " {}", op.name).unwrap(); + } + s.push_str("))"); fmt.line(&s); }); diff --git a/cranelift/codegen/meta/src/isle.rs b/cranelift/codegen/meta/src/isle.rs index 4f3bc325cb3b..04e9f38b6e04 100644 --- a/cranelift/codegen/meta/src/isle.rs +++ b/cranelift/codegen/meta/src/isle.rs @@ -1,3 +1,5 @@ +use std::io::Result; + /// A list of compilations (transformations from ISLE source to /// generated Rust source) that exist in the repository. /// @@ -26,18 +28,46 @@ impl IsleCompilations { pub struct IsleCompilation { pub name: String, pub output: std::path::PathBuf, - pub inputs: Vec, + pub tracked_inputs: Vec, pub untracked_inputs: Vec, } impl IsleCompilation { + /// All inputs to the computation, tracked or untracked. May contain directories. pub fn inputs(&self) -> Vec { - self.inputs + self.tracked_inputs .iter() .chain(self.untracked_inputs.iter()) .cloned() .collect() } + + /// All path inputs to the compilation. Directory inputs are expanded to the + /// list of all ISLE files in the directory. + pub fn paths(&self) -> Result> { + let mut paths = Vec::new(); + for input in self.inputs() { + paths.extend(Self::expand_paths(&input)?); + } + Ok(paths) + } + + fn expand_paths(input: &std::path::PathBuf) -> Result> { + if input.is_file() { + return Ok(vec![input.clone()]); + } + + let mut paths = Vec::new(); + for entry in std::fs::read_dir(input)? { + let path = entry?.path(); + if let Some(ext) = path.extension() { + if ext == "isle" { + paths.push(path); + } + } + } + Ok(paths) + } } pub fn shared_isle_lower_paths(codegen_crate_dir: &std::path::Path) -> Vec { @@ -67,6 +97,27 @@ pub fn get_isle_compilations( let prelude_lower_isle = codegen_crate_dir.join("src").join("prelude_lower.isle"); #[cfg(feature = "pulley")] let pulley_gen = gen_dir.join("pulley_gen.isle"); + // Verification + let prelude_spec_isle = codegen_crate_dir + .join("src") + .join("spec") + .join("prelude_spec.isle"); + let inst_specs_isle = codegen_crate_dir + .join("src") + .join("spec") + .join("inst_specs.isle"); + let inst_tags_isle = codegen_crate_dir + .join("src") + .join("spec") + .join("inst_tags.isle"); + let fpconst_isle = codegen_crate_dir + .join("src") + .join("spec") + .join("fpconst.isle"); + let state_isle = codegen_crate_dir + .join("src") + .join("spec") + .join("state.isle"); // Directory for mid-end optimizations. let src_opts = codegen_crate_dir.join("src").join("opts"); @@ -101,9 +152,12 @@ pub fn get_isle_compilations( IsleCompilation { name: "opt".to_string(), output: gen_dir.join("isle_opt.rs"), - inputs: vec![ + tracked_inputs: vec![ prelude_isle.clone(), prelude_opt_isle, + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), src_opts.join("arithmetic.isle"), src_opts.join("bitops.isle"), src_opts.join("cprop.isle"), @@ -123,9 +177,12 @@ pub fn get_isle_compilations( IsleCompilation { name: "x64".to_string(), output: gen_dir.join("isle_x64.rs"), - inputs: vec![ + tracked_inputs: vec![ prelude_isle.clone(), prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), src_isa_x64.join("inst.isle"), src_isa_x64.join("lower.isle"), ], @@ -139,11 +196,17 @@ pub fn get_isle_compilations( IsleCompilation { name: "aarch64".to_string(), output: gen_dir.join("isle_aarch64.rs"), - inputs: vec![ + tracked_inputs: vec![ prelude_isle.clone(), prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), + fpconst_isle.clone(), + state_isle.clone(), src_isa_aarch64.join("inst.isle"), src_isa_aarch64.join("inst_neon.isle"), + src_isa_aarch64.join("spec"), src_isa_aarch64.join("lower.isle"), src_isa_aarch64.join("lower_dynamic_neon.isle"), ], @@ -153,9 +216,12 @@ pub fn get_isle_compilations( IsleCompilation { name: "s390x".to_string(), output: gen_dir.join("isle_s390x.rs"), - inputs: vec![ + tracked_inputs: vec![ prelude_isle.clone(), prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), src_isa_s390x.join("inst.isle"), src_isa_s390x.join("lower.isle"), ], @@ -165,9 +231,12 @@ pub fn get_isle_compilations( IsleCompilation { name: "riscv64".to_string(), output: gen_dir.join("isle_riscv64.rs"), - inputs: vec![ + tracked_inputs: vec![ prelude_isle.clone(), prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), src_isa_risc_v.join("inst.isle"), src_isa_risc_v.join("inst_vector.isle"), src_isa_risc_v.join("lower.isle"), @@ -179,7 +248,7 @@ pub fn get_isle_compilations( IsleCompilation { name: "pulley".to_string(), output: gen_dir.join("isle_pulley_shared.rs"), - inputs: vec![ + tracked_inputs: vec![ prelude_isle.clone(), prelude_lower_isle.clone(), src_isa_pulley_shared.join("inst.isle"), diff --git a/cranelift/codegen/src/inst_specs.isle b/cranelift/codegen/src/inst_specs.isle deleted file mode 100644 index 83f703e4510c..000000000000 --- a/cranelift/codegen/src/inst_specs.isle +++ /dev/null @@ -1,242 +0,0 @@ -(model Imm64 (type (bv 64))) - -(model IntCC (enum - (Equal #x00) - (NotEqual #x01) - (SignedGreaterThan #x02) - (SignedGreaterThanOrEqual #x03) - (SignedLessThan #x04) - (SignedLessThanOrEqual #x05) - (UnsignedGreaterThan #x06) - (UnsignedGreaterThanOrEqual #x07) - (UnsignedLessThan #x08) - (UnsignedLessThanOrEqual #x09))) - -(spec (smin x y) - (provide (= result (if (bvsle x y) x y)))) -(instantiate smin bv_binary_8_to_64) - -(spec (umin x y) - (provide (= result (if (bvule x y) x y)))) -(instantiate umin bv_binary_8_to_64) - -(spec (smax x y) - (provide (= result (if (bvsge x y) x y)))) -(instantiate smax bv_binary_8_to_64) - -(spec (umax x y) - (provide (= result (if (bvuge x y) x y)))) -(instantiate umax bv_binary_8_to_64) - -(spec (iconst arg) - (provide (= arg (zero_ext 64 result)))) -(instantiate iconst - ((args (bv 64)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 64)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 64)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 64)) (canon (bv 64))) -) - -(spec (bitselect c x y) - (provide (= result (bvor (bvand c x) (bvand (bvnot c) y))))) -(instantiate bitselect bv_ternary_8_to_64) - -(spec (icmp c x y) - (provide - (= result - (switch c - ((IntCC.Equal) (if (= x y) #x01 #x00)) - ((IntCC.NotEqual) (if (not (= x y)) #x01 #x00)) - ((IntCC.SignedGreaterThan) (if (bvsgt x y) #x01 #x00)) - ((IntCC.SignedGreaterThanOrEqual) (if (bvsge x y) #x01 #x00)) - ((IntCC.SignedLessThan) (if (bvslt x y) #x01 #x00)) - ((IntCC.SignedLessThanOrEqual) (if (bvsle x y) #x01 #x00)) - ((IntCC.UnsignedGreaterThan) (if (bvugt x y) #x01 #x00)) - ((IntCC.UnsignedGreaterThanOrEqual) (if (bvuge x y) #x01 #x00)) - ((IntCC.UnsignedLessThan) (if (bvult x y) #x01 #x00)) - ((IntCC.UnsignedLessThanOrEqual) (if (bvule x y) #x01 #x00))))) - (require - ;; AVH TODO: if we understand enums semantically, we can generate this - (or - (= c (IntCC.Equal)) - (= c (IntCC.NotEqual)) - (= c (IntCC.UnsignedGreaterThanOrEqual)) - (= c (IntCC.UnsignedGreaterThan)) - (= c (IntCC.UnsignedLessThanOrEqual)) - (= c (IntCC.UnsignedLessThan)) - (= c (IntCC.SignedGreaterThanOrEqual)) - (= c (IntCC.SignedGreaterThan)) - (= c (IntCC.SignedLessThanOrEqual)) - (= c (IntCC.SignedLessThan))))) -(instantiate icmp - ((args (bv 8) (bv 8) (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 8) (bv 16) (bv 16)) (ret (bv 8)) (canon (bv 16))) - ((args (bv 8) (bv 32) (bv 32)) (ret (bv 8)) (canon (bv 32))) - ((args (bv 8) (bv 64) (bv 64)) (ret (bv 8)) (canon (bv 64))) -) - -(spec (iadd x y) - (provide (= result (bvadd x y)))) -(instantiate iadd bv_binary_8_to_64) - -(spec (isub x y) - (provide (= result (bvsub x y)))) -(instantiate isub bv_binary_8_to_64) - -(spec (ineg x) - (provide (= result (bvneg x)))) -(instantiate ineg bv_unary_8_to_64) - -(spec (iabs x) - (provide (= result - (if (bvsge x (conv_to (widthof x) #x0000000000000000)) - x - (bvneg x))))) -(instantiate iabs bv_unary_8_to_64) - -(spec (imul x y) - (provide (= result (bvmul x y)))) -(instantiate imul bv_binary_8_to_64) - -(spec (udiv x y) - (provide (= result (bvudiv x y))) - (require (not (= y (zero_ext (widthof y) #b0))))) -(instantiate udiv bv_binary_8_to_64) - -(spec (sdiv x y) - (provide (= result (bvsdiv x y))) - (require (not (= y (zero_ext (widthof y) #b0))))) -(instantiate sdiv bv_binary_8_to_64) - -(spec (urem x y) - (provide (= result (bvurem x y))) - (require (not (= y (zero_ext (widthof y) #b0))))) -(instantiate urem bv_binary_8_to_64) - -(spec (srem x y) - (provide (= result (bvsrem x y))) - (require (not (= y (zero_ext (widthof y) #b0))))) -(instantiate srem bv_binary_8_to_64) - -(spec (imul_imm x y) - (provide (= result (bvmul (sign_ext 64 x) y)))) - -(spec (band x y) - (provide (= result (bvand x y)))) -(instantiate band bv_binary_8_to_64) - -(spec (bor x y) - (provide (= result (bvor x y)))) -(instantiate bor bv_binary_8_to_64) - -(spec (bxor x y) - (provide (= result (bvxor x y)))) -(instantiate bxor bv_binary_8_to_64) - -(spec (bnot x) - (provide (= result (bvnot x))) - (require (or (= (widthof x) 8) (= (widthof x) 16) (= (widthof x) 32) (= (widthof x) 64)))) -(instantiate bnot bv_unary_8_to_64) - -(spec (band_not x y) - (provide (= result (bvand x (bvnot y))))) -(instantiate band_not bv_binary_8_to_64) - -(spec (rotl x y) - (provide (= result (rotl x y)))) -(instantiate rotl bv_binary_8_to_64) - -(spec (rotr x y) - (provide (= result (rotr x y)))) -(instantiate rotr bv_binary_8_to_64) - -;; fn shift_mask(&mut self, ty: Type) -> ImmLogic { -;; let mask = (ty.lane_bits() - 1) as u64; -;; ImmLogic::maybe_from_u64(mask, I32).unwrap() -;; } -(spec (ishl x y) - (provide - (= result - (bvshl x - (bvand (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) - #x0000000000000001)) - y))))) -(instantiate ishl bv_binary_8_to_64) - -(spec (ushr x y) - (provide - (= result - (bvlshr x - (bvand (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) - #x0000000000000001)) - y))))) -(instantiate ushr bv_binary_8_to_64) - -(spec (sshr x y) - (provide - (= result - (bvashr x - (bvand (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) - #x0000000000000001)) - y))))) -(instantiate sshr bv_binary_8_to_64) - -(spec (clz x) - (provide (= result (clz x)))) -(instantiate clz bv_unary_8_to_64) - -(spec (cls x) - (provide (= result (cls x)))) -(instantiate cls bv_unary_8_to_64) - -(spec (ctz x) - (provide (= result (clz (rev x))))) -(instantiate ctz bv_unary_8_to_64) - -(spec (popcnt x) - (provide (= result (popcnt x)))) -(instantiate popcnt bv_unary_8_to_64) - -(form extend - ((args (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 8)) (ret (bv 16)) (canon (bv 8))) - ((args (bv 8)) (ret (bv 32)) (canon (bv 8))) - ((args (bv 8)) (ret (bv 64)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 16)) (ret (bv 32)) (canon (bv 16))) - ((args (bv 16)) (ret (bv 64)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 64)) (canon (bv 64))) -) - -(spec (uextend x) - (provide (= result (zero_ext (widthof result) x)))) -(instantiate uextend extend) - -(spec (sextend x) - (provide (= result (sign_ext (widthof result) x)))) -(instantiate sextend extend) - - -(form load - ((args (bv 16) (bv 64) (bv 32)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16) (bv 64) (bv 32)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 16) (bv 64) (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 16) (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 64))) -) -(spec (load flags val offset) - (provide - (= result (load_effect flags (widthof result) (bvadd val (sign_ext 64 offset)))))) -(instantiate load load) - -(form store - ((args (bv 16) (bv 8) (bv 64) (bv 32)) (ret Unit) (canon (bv 8))) - ((args (bv 16) (bv 16) (bv 64) (bv 32)) (ret Unit) (canon (bv 16))) - ((args (bv 16) (bv 32) (bv 64) (bv 32)) (ret Unit) (canon (bv 32))) - ((args (bv 16) (bv 64) (bv 64) (bv 32)) (ret Unit) (canon (bv 64))) -) -(spec (store flags val_to_store addr offset) - (provide - (= result (store_effect flags (widthof val_to_store) val_to_store (bvadd (zero_ext 64 addr) (sign_ext 64 offset)))))) -(instantiate store store) diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index 6e3fbcd2f683..441cc782a30a 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -1,4 +1,12 @@ ;; Instruction formats. +(model MInst + (type + (struct + (flags_in (named NZCV)) + (flags_out (named NZCV)) + ) + ) +) (type MInst (enum ;; A no-op of zero size. @@ -193,7 +201,6 @@ (imm MoveWideConst) (size OperandSize)) - ;; A sign- or zero-extend operation. (Extend (rd WritableReg) @@ -390,7 +397,6 @@ (ri Reg) (rn Reg)) - ;; 3-op FPU instruction. ;; 16-bit scalars require half-precision floating-point support (FEAT_FP16). (FpuRRRR @@ -1009,22 +1015,195 @@ (end Reg) (step Imm12)))) -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (Extr #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) +(spec (MInst.AluRRImmLogic alu_op size rd rn imml) + (provide + (=> + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size64))) + (= rd (bvor rn imml)) + ) + (=> + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvor (extract 31 0 rn) (extract 31 0 imml)))) + ) + + (=> + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size64))) + (= rd (bvor rn (bvnot imml))) + ) + (=> + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvor (extract 31 0 rn) (bvnot (extract 31 0 imml))))) + ) + + (=> + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size64))) + (= rd (bvand rn imml)) + ) + (=> + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvand (extract 31 0 rn) (extract 31 0 imml)))) + ) + + (=> + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size64))) + (= rd (bvand rn (bvnot imml))) + ) + (=> + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvand (extract 31 0 rn) (bvnot (extract 31 0 imml))))) + ) + + (=> + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size64))) + (= rd (bvxor rn imml)) + ) + (=> + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvxor (extract 31 0 rn) (extract 31 0 imml)))) + ) + + (=> + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size64))) + (= rd (bvxor rn (bvnot imml))) + ) + (=> + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvxor (extract 31 0 rn) (bvnot (extract 31 0 imml))))) + ) + ) + (require + ; Implemented (opcode, size) combinations. + (or + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size32))) + ) + ) +) + +(spec (MInst.AluRRImmShift alu_op size rd rn immshift) + (provide + (=> (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size64))) + (= rd (bvlshr rn (zero_ext 64 immshift))) + ) + (=> (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvlshr (extract 31 0 rn) (zero_ext 32 immshift)))) + ) + + (=> (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size64))) + (= rd (bvashr rn (zero_ext 64 immshift))) + ) + (=> (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvashr (extract 31 0 rn) (zero_ext 32 immshift)))) + ) + + (=> (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size64))) + (= rd (bvshl rn (zero_ext 64 immshift))) + ) + (=> (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvshl (extract 31 0 rn) (zero_ext 32 immshift)))) + ) + + (=> (and (= alu_op (ALUOp.Extr)) (= size (OperandSize.Size32))) + (with + (t1 t3 t4 t5) + (and + (= t1 (= (extract 4 0 immshift) #b00000)) + (if + t1 + (= t3 (extract 31 0 rn)) + (and + (not (= (extract 4 0 immshift) #b00000)) + (= + t4 + (bvor + (bvlshr (extract 31 0 rn) (zero_ext 32 (extract 4 0 immshift))) + (bvshl + (extract 31 0 rn) + (zero_ext 32 (bvsub #x20 (zero_ext 8 (extract 4 0 immshift))))))))) + (= t5 (if t1 t3 t4)) + (= rd (zero_ext 64 t5)))) + ) + (=> (and (= alu_op (ALUOp.Extr)) (= size (OperandSize.Size64))) + (with + (t1 t3 t4 t5) + (and + (= t1 (= (extract 5 0 immshift) #b000000)) + (if + t1 + (= t3 rn) + (and + (not (= (extract 5 0 immshift) #b000000)) + (= + t4 + (bvor + (bvlshr rn (zero_ext 64 (extract 5 0 immshift))) + (bvshl rn (zero_ext 64 (bvsub #x40 (zero_ext 8 (extract 5 0 immshift))))))))) + (= t5 (if t1 t3 t4)) + (= rd t5)))) + ) + (require + (or + (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.Extr)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Extr)) (= size (OperandSize.Size32))) + ) + ) +) + +(spec (MInst.TrapIf kind trap_code) + (modifies exec_trap trap_cond) + (provide + ; Conditions under which this instruction traps. + ; + ; The Zero / NotZero kinds use a cbz/cbnz instruction whose operand + ; size selects whether the 32-bit or 64-bit view of `r` is tested. + ; Registers are modeled at 64 bits with arbitrary padding above the + ; significant bits (see `put_in_reg`), so we must mask to the low + ; `s` bits before checking for zero. + (= trap_cond + (match kind + ((Zero r s) + (if (Size32? s) + (bv_is_zero! (extract 31 0 r)) + (bv_is_zero! r))) + ((NotZero r s) + (if (Size32? s) + (not (bv_is_zero! (extract 31 0 r))) + (not (bv_is_zero! r)))) + ((Cond cc) (cond_holds! cc (:flags_in result))) + ) + ) + + ; If this instruction traps, set the global trap state. + (=> trap_cond exec_trap) + ) +) + +;; Leaves currently without specs: excluded from verification via todo. +(attr MInst.Udf (tag TODO)) ;; An ALU operation. This can be paired with several instruction formats ;; below (see `Inst`) in any combination. @@ -1084,43 +1263,98 @@ (MovN) )) +(model UImm5 (type (bv 5))) (type UImm5 (primitive UImm5)) -(model Imm12 (type (bv 24))) + +(model Imm12 + (type + (struct + (bits (bv 12)) + (shift12 Bool) + ) + ) +) (type Imm12 (primitive Imm12)) + (model ImmLogic (type (bv 64))) (type ImmLogic (primitive ImmLogic)) + (model ImmShift (type (bv 6))) (type ImmShift (primitive ImmShift)) -(model ShiftOpAndAmt (type (bv 16))) + +(model ShiftOpAndAmt + (type + (struct + (op (named ALUOp)) + (amt (bv 8)) + ) + ) +) (type ShiftOpAndAmt (primitive ShiftOpAndAmt)) -(model MoveWideConst (type (bv 16))) + +(model MoveWideConst + (type + (struct + (shift (bv 2)) + (bits (bv 16)) + ) + ) +) (type MoveWideConst (primitive MoveWideConst)) + +(model NZCV + (type + (struct + (N (bv 1)) + (Z (bv 1)) + (C (bv 1)) + (V (bv 1)) + ) + ) +) (type NZCV (primitive NZCV)) + +(model ASIMDFPModImm (type + (struct + (imm (bv 8)) + (size (named ScalarSize)) + ) +)) (type ASIMDFPModImm (primitive ASIMDFPModImm)) + +(model ASIMDMovModImm (type + (struct + (imm (bv 8)) + ; TODO: shift: u8, + ; TODO: is_64bit: bool, + ; TODO: shift_ones: bool, + ) +)) (type ASIMDMovModImm (primitive ASIMDMovModImm)) + +(model SImm7Scaled (type !)) (type SImm7Scaled (primitive SImm7Scaled)) (type BoxCallInfo (primitive BoxCallInfo)) (type BoxCallIndInfo (primitive BoxCallIndInfo)) (type BoxReturnCallInfo (primitive BoxReturnCallInfo)) (type BoxReturnCallIndInfo (primitive BoxReturnCallIndInfo)) -(type CondBrKind (primitive CondBrKind)) (type BranchTarget (primitive BranchTarget)) (type BoxJTSequenceInfo (primitive BoxJTSequenceInfo)) (type CodeOffset (primitive CodeOffset)) (type VecMachLabel extern (enum)) -(model ExtendOp (enum - (UXTB #b000) - (UXTH #b001) - (UXTW #b010) - (UXTX #b011) - (SXTB #b100) - (SXTH #b101) - (SXTW #b110) - (SXTX #b111) +(type CondBrKind extern + (enum + (Zero (r Reg) (s OperandSize)) + (NotZero (r Reg) (s OperandSize)) + (Cond (cc Cond)) )) +; Expect that branched on registers will be general-purpose registers. +(instantiate CondBrKind.Zero ((args (bv 64) (named OperandSize)) (ret (named CondBrKind)))) +(instantiate CondBrKind.NotZero ((args (bv 64) (named OperandSize)) (ret (named CondBrKind)))) + (type ExtendOp extern (enum (UXTB) @@ -1147,8 +1381,13 @@ (Rev64) )) +(model MemLabel (type !)) (type MemLabel extern (enum)) + +(model SImm9 (type (bv 9))) (type SImm9 extern (enum)) + +(model UImm12Scaled (type (bv 12))) (type UImm12Scaled extern (enum)) ;; An addressing mode specified for a load/store operation. @@ -1253,6 +1492,18 @@ (SlotOffset (off i64)))) +(attr AMode.Const (tag amode_const)) + +(instantiate AMode.RegReg ((args (bv 64) (bv 64)) (ret (named AMode)))) + +(instantiate AMode.RegScaled ((args (bv 64) (bv 64)) (ret (named AMode)))) + +(instantiate AMode.RegScaledExtended ((args (bv 64) (bv 64) (named ExtendOp)) (ret (named AMode)))) + +(instantiate AMode.RegExtended ((args (bv 64) (bv 64) (named ExtendOp)) (ret (named AMode)))) + +(instantiate AMode.RegOffset ((args (bv 64) (named i64)) (ret (named AMode)))) + ;; A memory argument to a load/store-pair. (type PairAMode (enum ;; Signed, scaled 7-bit offset from a register. @@ -1267,12 +1518,23 @@ (SPPostIndexed (simm7 SImm7Scaled)) )) +(model FPUOpRI (type + (struct + (size (bv 8)) + (amount (bv 8)) + (lane_size_in_bits (bv 8)) + ) +)) (type FPUOpRI extern (enum)) -(type FPUOpRIMod extern (enum)) -(model OperandSize - (enum (Size32 32) - (Size64 64))) +(model FPUOpRIMod (type + (struct + (size (bv 8)) + (amount (bv 8)) + (lane_size_in_bits (bv 8)) + ) +)) +(type FPUOpRIMod extern (enum)) (type OperandSize extern (enum Size32 @@ -1283,28 +1545,18 @@ ;; Helper for calculating the `OperandSize` corresponding to a type (spec (operand_size ty) (provide - (= result (if (<= ty 32) 32 64))) + (= result (if (<= (:bits ty) 32) (OperandSize.Size32) (OperandSize.Size64)))) (require - (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) -(instantiate operand_size - ((args Int) (ret Int) (canon (bv 8))) - ((args Int) (ret Int) (canon (bv 16))) - ((args Int) (ret Int) (canon (bv 32))) - ((args Int) (ret Int) (canon (bv 64))) -) + (or (= (:bits ty) 8) (= (:bits ty) 16) (= (:bits ty) 32) (= (:bits ty) 64)))) (decl operand_size (Type) OperandSize) + +(attr rule operand_size_32 (veri priority)) (rule operand_size_32 1 (operand_size (fits_in_32 _ty)) (OperandSize.Size32)) (rule operand_size_64 (operand_size (fits_in_64 _ty)) (OperandSize.Size64)) -(model ScalarSize - (enum (Size8 8) - (Size16 16) - (Size32 32) - (Size64 64) - (Size128 128))) - ;; Difference (32 - ty), useful for narrow calculations with 32-bit ;; instructions. +(attr diff_from_32 (veri chain)) (decl diff_from_32 (Type) u8) (rule (diff_from_32 $I8) 24) (rule (diff_from_32 $I16) 16) @@ -1317,6 +1569,17 @@ Size128)) ;; Helper for calculating the `ScalarSize` corresponding to a type +(spec (scalar_size ty) + (provide (= result + (switch (:bits ty) + (8 (ScalarSize.Size8)) + (16 (ScalarSize.Size16)) + (32 (ScalarSize.Size32)) + (64 (ScalarSize.Size64)) + (128 (ScalarSize.Size128)) + ) + )) +) (decl scalar_size (Type) ScalarSize) (rule (scalar_size $I8) (ScalarSize.Size8)) @@ -1329,6 +1592,7 @@ (rule (scalar_size $F64) (ScalarSize.Size64)) ;; Helper for calculating the `ScalarSize` lane type from vector type +(attr lane_size (tag vector)) (decl lane_size (Type) ScalarSize) (rule 1 (lane_size (multi_lane 8 _)) (ScalarSize.Size8)) (rule 1 (lane_size (multi_lane 16 _)) (ScalarSize.Size16)) @@ -1340,6 +1604,7 @@ (rule (lane_size (dynamic_lane 64 _)) (ScalarSize.Size64)) ;; Helper for extracting the size of a lane from the input `VectorSize` +(attr vector_lane_size (veri chain)) (decl pure vector_lane_size (VectorSize) ScalarSize) (rule (vector_lane_size (VectorSize.Size8x16)) (ScalarSize.Size8)) (rule (vector_lane_size (VectorSize.Size8x8)) (ScalarSize.Size8)) @@ -1349,12 +1614,6 @@ (rule (vector_lane_size (VectorSize.Size32x2)) (ScalarSize.Size32)) (rule (vector_lane_size (VectorSize.Size64x2)) (ScalarSize.Size64)) -(model Cond - (enum (Lo #x03) - (Hi #x08) - (Lt #x0b) - (Gt #x0c))) - (type Cond extern (enum (Eq) @@ -1375,16 +1634,6 @@ (Nv) )) -(model VectorSize - (enum - (Size8x8 #x00) - (Size8x16 #x01) - (Size16x4 #x02) - (Size16x8 #x03) - (Size32x2 #x04) - (Size32x4 #x05) - (Size64x2 #x06))) - (type VectorSize extern (enum (Size8x8) @@ -1397,6 +1646,7 @@ )) ;; Helper for calculating the `VectorSize` corresponding to a type +(attr vector_size (tag vector)) (decl vector_size (Type) VectorSize) (rule 1 (vector_size (multi_lane 8 8)) (VectorSize.Size8x8)) (rule 1 (vector_size (multi_lane 8 16)) (VectorSize.Size8x16)) @@ -1414,6 +1664,7 @@ (rule (vector_size (dynamic_lane 64 2)) (VectorSize.Size64x2)) ;; Helper for converting the `ScalarSize` of a float value to the corresponding `VectorSize` +(attr float_vector_size_in_64 (veri chain)) (decl float_vector_size_in_64 (Type) VectorSize) (rule (float_vector_size_in_64 $F16) (VectorSize.Size16x4)) (rule (float_vector_size_in_64 $F32) (VectorSize.Size32x2)) @@ -1819,6 +2070,7 @@ (decl use_lse () Inst) (extern extractor use_lse use_lse) +(spec (use_fp16) (provide (not result))) (decl pure use_fp16 () bool) (extern constructor use_fp16 use_fp16) @@ -1826,56 +2078,144 @@ (extern constructor use_csdb use_csdb) ;; Extractor helpers for various immediate constants ;;;;;;;;;;;;;;;;;;;;;;;;;; - +(spec (move_wide_const_from_u64 ty n) + (provide + (let + ( + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (equals_move_wide_value! nlow result) + ) + ) + (match + (let + ( + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (or + (= nlow (bvand nlow #x000000000000ffff)) + (= nlow (bvand nlow #x00000000ffff0000)) + (= nlow (bvand nlow #x0000ffff00000000)) + (= nlow (bvand nlow #xffff000000000000)) + ) + ) + ) +) (decl pure partial move_wide_const_from_u64 (Type u64) MoveWideConst) (extern constructor move_wide_const_from_u64 move_wide_const_from_u64) +(spec (move_wide_const_from_inverted_u64 ty n_inverted) + (provide + (let + ( + (n (bvnot n_inverted)) + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (equals_move_wide_value! nlow result) + ) + ) + (match + (let + ( + (n (bvnot n_inverted)) + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (or + (= nlow (bvand nlow #x000000000000ffff)) + (= nlow (bvand nlow #x00000000ffff0000)) + (= nlow (bvand nlow #x0000ffff00000000)) + (= nlow (bvand nlow #xffff000000000000)) + ) + ) + ) +) (decl pure partial move_wide_const_from_inverted_u64 (Type u64) MoveWideConst) (extern constructor move_wide_const_from_inverted_u64 move_wide_const_from_inverted_u64) +(spec (imm_logic_from_u64 ty n) + (provide (= result n)) + (match (or (= (:bits ty) 32) (= (:bits ty) 64))) +) (decl pure partial imm_logic_from_u64 (Type u64) ImmLogic) (extern constructor imm_logic_from_u64 imm_logic_from_u64) +(spec (imm_size_from_type ty) + (match (or (= (:bits ty) 32) (= (:bits ty) 64))) + (provide (= result (int2bv 16 (:bits ty)))) +) (decl pure partial imm_size_from_type (Type) u16) (extern constructor imm_size_from_type imm_size_from_type) +(spec (imm_logic_from_imm64 ty imm64) + (provide (= result imm64)) + (match (or (= (:bits ty) 32) (= (:bits ty) 64)))) (decl pure partial imm_logic_from_imm64 (Type Imm64) ImmLogic) (extern constructor imm_logic_from_imm64 imm_logic_from_imm64) -(spec (imm_shift_from_imm64 ty x) - (provide (= result (extract 5 0 (bvand x (bvsub (int2bv 64 ty) #x0000000000000001))))) - (require (bvult (bvand x (bvsub (int2bv 64 ty) #x0000000000000001)) #x0000000000000040))) - +(spec (imm_shift_from_imm64 ty v) + (provide + (let + ((imm_value (bvand v (shift_mask! 64 (:bits ty))))) + (= result (extract 5 0 imm_value)) + ) + ) + (match + (let + ((imm_value (bvand v (shift_mask! 64 (:bits ty))))) + (bvult imm_value (int2bv 64 64)) + ) + ) +) (decl pure partial imm_shift_from_imm64 (Type Imm64) ImmShift) (extern constructor imm_shift_from_imm64 imm_shift_from_imm64) +(spec (imm_shift_from_u8 n) + (provide (= result (extract 5 0 n))) + (require (bvult n #x40)) +) (decl imm_shift_from_u8 (u8) ImmShift) (extern constructor imm_shift_from_u8 imm_shift_from_u8) (spec (imm12_from_u64 imm12) - (provide (= result (zero_ext 64 imm12))) - (require - ; REVIEW(mbm): correct formulation of imm12? + (provide + (= result + (zero_ext 64 + (if (:shift12 imm12) + (concat (:bits imm12) #x000) + (concat #x000 (:bits imm12)) + ) + ) + ) + ) + (match (or - (= imm12 (bvand imm12 #x000fff)) - (= imm12 (bvand imm12 #xfff000)) + (= result (bvand result (zero_ext 64 #x000fff))) + (= result (bvand result (zero_ext 64 #xfff000))) ) ) ) (decl imm12_from_u64 (Imm12) u64) (extern extractor imm12_from_u64 imm12_from_u64) +(spec (u8_into_uimm5 n) + (provide (= result (extract 4 0 n))) + (require (bvult n #x20)) +) (decl u8_into_uimm5 (u8) UImm5) (extern constructor u8_into_uimm5 u8_into_uimm5) -(spec (u8_into_imm12 arg) - (provide (= result (zero_ext 24 arg)))) +(spec (u8_into_imm12 n) + (provide + (not (:shift12 result)) + (= (:bits result) (zero_ext 12 n)) + ) +) (decl u8_into_imm12 (u8) Imm12) (extern constructor u8_into_imm12 u8_into_imm12) (spec (u64_into_imm_logic ty a) (provide (= result a)) - (require (or (= ty 32) (= ty 64)))) + (require (or (= (:bits ty) 32) (= (:bits ty) 64)))) (decl u64_into_imm_logic (Type u64) ImmLogic) (extern constructor u64_into_imm_logic u64_into_imm_logic) @@ -1904,20 +2244,49 @@ ;; Constructs an FPUOpRI.Ushr* given the size in bits of the value (or lane) ;; and the amount to shift by. +(spec (fpu_op_ri_ushr ty_bits shift) + (provide + (= (:size result) ty_bits) + (= (:amount result) shift) + (= (:lane_size_in_bits result) ty_bits) + ) +) (decl fpu_op_ri_ushr (u8 u8) FPUOpRI) (extern constructor fpu_op_ri_ushr fpu_op_ri_ushr) ;; Constructs an FPUOpRIMod.Sli* given the size in bits of the value (or lane) ;; and the amount to shift by. +(spec (fpu_op_ri_sli ty_bits shift) + (provide + (= (:size result) ty_bits) + (= (:amount result) shift) + (= (:lane_size_in_bits result) ty_bits) + ) +) (decl fpu_op_ri_sli (u8 u8) FPUOpRIMod) (extern constructor fpu_op_ri_sli fpu_op_ri_sli) (decl pure partial lshr_from_u64 (Type u64) ShiftOpAndAmt) (extern constructor lshr_from_u64 lshr_from_u64) -(spec (lshl_from_imm64 ty a) - (provide (= result (concat #x0e (extract 7 0 a)))) - (require (= (extract 63 8 a) #b00000000000000000000000000000000000000000000000000000000))) +(spec (lshl_from_imm64 ty imm) + (provide + (= (:op result) (ALUOp.Lsl)) + (= (:amt result) + ; imm & (ty - 1) + (bvand + (extract 7 0 imm) + (bvsub (int2bv 8 (:bits ty)) #x01) + ) + ) + ) + (match + ; imm < 64 + (bvult imm #x0000000000000040) + ; ty <= 255 + (<= (:bits ty) 255) + ) +) (decl pure partial lshl_from_imm64 (Type Imm64) ShiftOpAndAmt) (extern constructor lshl_from_imm64 lshl_from_imm64) @@ -1927,9 +2296,17 @@ (decl pure partial ashr_from_u64 (Type u64) ShiftOpAndAmt) (extern constructor ashr_from_u64 ashr_from_u64) +(spec (integral_ty ty) + (provide (= result ty)) + (match (or + (= (:bits result) 8) + (= (:bits result) 16) + (= (:bits result) 32) + (= (:bits result) 64)))) (decl integral_ty (Type) Type) (extern extractor integral_ty integral_ty) +(attr valid_atomic_transaction (tag TODO)) (decl valid_atomic_transaction (Type) Type) (extern extractor valid_atomic_transaction valid_atomic_transaction) @@ -1940,19 +2317,28 @@ (extern constructor is_zero_uimm12 is_zero_uimm12) ;; Helper to go directly from a `Value`, when it's an `iconst`, to an `Imm12`. -; REVIEW(mbm): is imm12_from_value spec correct? -; NOTE(mbm): compare with https://github.com/avanhatt/wasmtime/blob/94ccb9d4d55a479893cb04bc796ec620ed24cee2/cranelift/codegen/src/isa/aarch64/inst.isle#L1867-L1874 (spec (imm12_from_value imm12) (provide - ; REVIEW(mbm): zero_ext vs conv_to? - (= result (conv_to (widthof result) (zero_ext 64 imm12))) - (= imm12 (conv_to (widthof imm12) (zero_ext 64 result))) + (= + (zero_ext 128 result) + (zero_ext 128 + (if (:shift12 imm12) + (concat (:bits imm12) #x000) + (concat #x000 (:bits imm12)) + ) + ) + ) ) - (require - ; REVIEW(mbm): correct formulation of imm12? - (or - (= imm12 (bvand imm12 #x000fff)) - (= imm12 (bvand imm12 #xfff000)) + (match + (let + ( + (width 128) + (r (zero_ext width result)) + ) + (or + (= r (bvand r (zero_ext width #x000fff))) + (= r (bvand r (zero_ext width #xfff000))) + ) ) ) ) @@ -1962,25 +2348,7 @@ (iconst _ (u64_from_imm64 (imm12_from_u64 n)))) ;; Conceptually the same as `imm12_from_value`, but tries negating the constant ;; value (first sign-extending to handle narrow widths). -(spec (imm12_from_negated_value arg) - (provide - (= (bvneg (sign_ext 64 arg)) (zero_ext 64 result)) - ) - (require - ; REVIEW(mbm): correct formulation of imm12? - (or - (= result (bvand result #x000fff)) - (= result (bvand result #xfff000)) - ) - ) -) - -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) +(attr imm12_from_negated_value (veri chain)) (decl pure partial imm12_from_negated_value (Value) Imm12) (rule imm12_from_negated_value (imm12_from_negated_value (has_type ty (iconst _ n))) @@ -1988,48 +2356,83 @@ imm) ;; Helper type to represent a value and an extend operation fused together. -(model ExtendedValue (type (bv 67))) +(model ExtendedValue (type + (struct + (val (named Value)) + (extend (named ExtendOp)) + ) +)) (type ExtendedValue extern (enum)) ;; Only including the i8 to i32 opcodes, based on the impl of extended_value_from_value (spec (extended_value_from_value x) - (provide - (switch (extract 66 64 x) - ((ExtendOp.UXTB) (= (extract 63 0 x) (zero_ext 64 (extract 7 0 (zero_ext 64 result))))) - ((ExtendOp.UXTH) (= (extract 63 0 x) (zero_ext 64 (extract 15 0 (zero_ext 64 result))))) - ((ExtendOp.UXTW) (= (extract 63 0 x) (zero_ext 64 (extract 31 0 (zero_ext 64 result))))) - ((ExtendOp.SXTB) (= (extract 63 0 x) (sign_ext 64 (extract 7 0 (zero_ext 64 result))))) - ((ExtendOp.SXTH) (= (extract 63 0 x) (sign_ext 64 (extract 15 0 (zero_ext 64 result))))) - ((ExtendOp.SXTW) (= (extract 63 0 x) (sign_ext 64 (extract 31 0 (zero_ext 64 result))))))) - (require - (bvult (extract 66 64 x) #b110) - (not (= (extract 66 64 x) #b011)) - (= result (conv_to (widthof result) x)) - (or (= 8 (widthof result)) (= 16 (widthof result)) (= 32 (widthof result))))) + ; Match when the result can be represented as an extend. + (match + (or + (with (b) (is_ext! result (as b (bv 8)))) + (with (h) (is_ext! result (as h (bv 16)))) + (with (w) (is_ext! result (as w (bv 32)))) + (with (x) (is_ext! result (as x (bv 64)))) + ) + ) + + ; Determine the ExtendedValue parameters. + (provide + ; Width of the input. + (= (widthof (:val x)) (extend_op_src_width! (:extend x))) + + ; Result equals the extension. + (if (extend_op_signed! (:extend x)) + (is_sign_ext! result (:val x)) + (is_zero_ext! result (:val x)) + ) + ) +) +(instantiate extended_value_from_value + ((args (struct (val (bv 8)) (extend (named ExtendOp)))) (ret (named Value))) + ((args (struct (val (bv 16)) (extend (named ExtendOp)))) (ret (named Value))) + ((args (struct (val (bv 32)) (extend (named ExtendOp)))) (ret (named Value))) + ((args (struct (val (bv 64)) (extend (named ExtendOp)))) (ret (named Value))) +) (decl extended_value_from_value (ExtendedValue) Value) (extern extractor extended_value_from_value extended_value_from_value) ;; Constructors used to poke at the fields of an `ExtendedValue`. +(spec (put_extended_in_reg x) + (provide (= result (conv_to (widthof result) (:val x)))) +) (decl put_extended_in_reg (ExtendedValue) Reg) (extern constructor put_extended_in_reg put_extended_in_reg) + +(spec (get_extended_op x) (provide (= result (:extend x)))) (decl get_extended_op (ExtendedValue) ExtendOp) (extern constructor get_extended_op get_extended_op) +(spec (nzcv n z c v) + (provide + (= (:N result) (bool2bit! n)) + (= (:Z result) (bool2bit! z)) + (= (:C result) (bool2bit! c)) + (= (:V result) (bool2bit! v)) + ) +) (decl nzcv (bool bool bool bool) NZCV) (extern constructor nzcv nzcv) +(spec (cond_br_zero r s) (provide (= result (CondBrKind.Zero r s)))) (decl cond_br_zero (Reg OperandSize) CondBrKind) (extern constructor cond_br_zero cond_br_zero) (decl cond_br_not_zero (Reg OperandSize) CondBrKind) (extern constructor cond_br_not_zero cond_br_not_zero) +(spec (cond_br_cond cc) (provide (= result (CondBrKind.Cond cc)))) (decl cond_br_cond (Cond) CondBrKind) (extern constructor cond_br_cond cond_br_cond) ;; Instruction creation helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helper for creating the zero register. -(spec (zero_reg) (provide (= result #x0000000000000000))) +(spec (zero_reg) (provide (= (zero_ext (widthof result) #b0) result))) (decl zero_reg () Reg) (extern constructor zero_reg zero_reg) @@ -2042,16 +2445,17 @@ (decl writable_link_reg () WritableReg) (extern constructor writable_link_reg writable_link_reg) +(spec (writable_zero_reg) (provide (= result #x00000000))) (decl writable_zero_reg () WritableReg) (extern constructor writable_zero_reg writable_zero_reg) +(attr value_regs_zero (veri chain)) (decl value_regs_zero () ValueRegs) (rule (value_regs_zero) (value_regs (imm $I64 (ImmExtend.Zero) 0) (imm $I64 (ImmExtend.Zero) 0))) - ;; Helper for emitting `MInst.Mov` instructions. (decl mov (Reg Type) Reg) (rule (mov src ty) @@ -2060,6 +2464,7 @@ dst)) ;; Helper for emitting `MInst.MovZ` instructions. +(attr movz (veri chain)) (decl movz (MoveWideConst OperandSize) Reg) (rule (movz imm size) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2067,6 +2472,7 @@ dst)) ;; Helper for emitting `MInst.MovN` instructions. +(attr movn (veri chain)) (decl movn (MoveWideConst OperandSize) Reg) (rule (movn imm size) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2074,6 +2480,7 @@ dst)) ;; Helper for emitting `MInst.AluRRImmLogic` instructions. +(attr alu_rr_imm_logic (veri chain)) (decl alu_rr_imm_logic (ALUOp Type Reg ImmLogic) Reg) (rule (alu_rr_imm_logic op ty src imm) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2081,24 +2488,7 @@ dst)) ;; Helper for emitting `MInst.AluRRImmShift` instructions. -(spec (alu_rr_imm_shift op t a b) - (provide - (= result (switch op - ((ALUOp.Lsr) - (if (<= t 32) - (conv_to 64 (bvlshr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 (zero_ext 64 b))))) - (bvlshr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) (zero_ext 64 b))))) - ((ALUOp.Asr) - (if (<= t 32) - (conv_to 64 (bvashr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 (zero_ext 64 b))))) - (bvashr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) (zero_ext 64 b))))) - ((ALUOp.Lsl) - (if (<= t 32) - (conv_to 64 (bvshl (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 (zero_ext 64 b))))) - (bvshl a (bvand (bvsub (int2bv 64 64) #x0000000000000001) (zero_ext 64 b)))))))) - (require - (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl))) - (or (= t 8) (= t 16) (= t 32) (= t 64)))) +(attr alu_rr_imm_shift (veri chain)) (decl alu_rr_imm_shift (ALUOp Type Reg ImmShift) Reg) (rule (alu_rr_imm_shift op ty src imm) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2106,24 +2496,7 @@ dst)) ;; Helper for emitting `MInst.AluRRR` instructions. -(spec (alu_rrr op t a b) - (provide - (= result (switch op - ((ALUOp.Lsr) - (if (<= t 32) - (conv_to 64 (bvlshr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b)))) - (bvlshr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b)))) - ((ALUOp.Asr) - (if (<= t 32) - (conv_to 64 (bvashr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b)))) - (bvashr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b)))) - ((ALUOp.Lsl) - (if (<= t 32) - (conv_to 64 (bvshl (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b)))) - (bvshl a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b))))))) - (require - (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl))) - (or (= t 8) (= t 16) (= t 32) (= t 64)))) +(attr alu_rrr (veri chain)) (decl alu_rrr (ALUOp Type Reg Reg) Reg) (rule (alu_rrr op ty src1 src2) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2138,6 +2511,7 @@ dst)) ;; Helper for emitting `MInst.FpuRR` instructions. +(attr fpu_rr (veri chain)) (decl fpu_rr (FPUOp1 Reg ScalarSize) Reg) (rule (fpu_rr op src size) (let ((dst WritableReg (temp_writable_reg $F64)) @@ -2160,12 +2534,14 @@ (_1 Unit (emit (MInst.VecFmlaElem op dst src1 src2 src3 size idx)))) dst)) +(attr fpu_rri (veri chain)) (decl fpu_rri (FPUOpRI Reg) Reg) (rule (fpu_rri op src) (let ((dst WritableReg (temp_writable_reg $F64)) (_ Unit (emit (MInst.FpuRRI op dst src)))) dst)) +(attr fpu_rri_mod (veri chain)) (decl fpu_rri_mod (FPUOpRIMod Reg Reg) Reg) (rule (fpu_rri_mod op dst_src src) (let ((dst WritableReg (temp_writable_reg $F64)) @@ -2173,6 +2549,7 @@ dst)) ;; Helper for emitting `MInst.FpuRRR` instructions. +(attr fpu_rrr (veri chain)) (decl fpu_rrr (FPUOp2 Reg Reg ScalarSize) Reg) (rule (fpu_rrr op src1 src2 size) (let ((dst WritableReg (temp_writable_reg $F64)) @@ -2187,6 +2564,7 @@ dst)) ;; Helper for emitting `MInst.FpuCmp` instructions. +(attr fpu_cmp (veri chain)) (decl fpu_cmp (ScalarSize Reg Reg) ProducesFlags) (rule (fpu_cmp size rn rm) (ProducesFlags.ProducesFlagsSideEffect @@ -2228,6 +2606,7 @@ dst)) ;; Helper for emitting `MInst.AluRRImm12` instructions. +(attr alu_rr_imm12 (veri chain)) (decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) (rule (alu_rr_imm12 op ty src imm) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2235,6 +2614,7 @@ dst)) ;; Helper for emitting `MInst.AluRRRShift` instructions. +(attr alu_rrr_shift (veri chain)) (decl alu_rrr_shift (ALUOp Type Reg Reg ShiftOpAndAmt) Reg) (rule (alu_rrr_shift op ty src1 src2 shift) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2243,6 +2623,7 @@ ;; Helper for emitting `cmp` instructions, setting flags, with a right-shifted ;; second operand register. +(attr cmp_rr_shift (veri chain)) (decl cmp_rr_shift (OperandSize Reg Reg u64) ProducesFlags) (rule (cmp_rr_shift size src1 src2 shift_amount) (if-let shift (lshr_from_u64 $I64 shift_amount)) @@ -2252,6 +2633,7 @@ ;; Helper for emitting `cmp` instructions, setting flags, with an arithmetic right-shifted ;; second operand register. +(attr cmp_rr_shift_asr (veri chain)) (decl cmp_rr_shift_asr (OperandSize Reg Reg u64) ProducesFlags) (rule (cmp_rr_shift_asr size src1 src2 shift_amount) (if-let shift (ashr_from_u64 $I64 shift_amount)) @@ -2260,6 +2642,7 @@ src1 src2 shift))) ;; Helper for emitting `MInst.AluRRRExtend` instructions. +(attr alu_rrr_extend (veri chain)) (decl alu_rrr_extend (ALUOp Type Reg Reg ExtendOp) Reg) (rule (alu_rrr_extend op ty src1 src2 extend) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2268,6 +2651,7 @@ ;; Same as `alu_rrr_extend`, but takes an `ExtendedValue` packed "pair" instead ;; of a `Reg` and an `ExtendOp`. +(attr alu_rr_extend_reg (veri chain)) (decl alu_rr_extend_reg (ALUOp Type Reg ExtendedValue) Reg) (rule (alu_rr_extend_reg op ty src1 extended_reg) (let ((src2 Reg (put_extended_in_reg extended_reg)) @@ -2275,6 +2659,7 @@ (alu_rrr_extend op ty src1 src2 extend))) ;; Helper for emitting `MInst.AluRRRR` instructions. +(attr alu_rrrr (veri chain)) (decl alu_rrrr (ALUOp3 Type Reg Reg Reg) Reg) (rule (alu_rrrr op ty src1 src2 src3) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2282,6 +2667,7 @@ dst)) ;; Helper for emitting paired `MInst.AluRRR` instructions +(attr alu_rrr_with_flags_paired (veri chain)) (decl alu_rrr_with_flags_paired (Type Reg Reg ALUOp) ProducesFlags) (rule (alu_rrr_with_flags_paired ty src1 src2 alu_op) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2298,12 +2684,14 @@ dst))) ;; Helper to create a `sbcs` instruction with no destination register. +(attr sbcs_side_effect (veri chain)) (decl sbcs_side_effect (Type Reg Reg) ProducesFlags) (rule (sbcs_side_effect ty src1 src2) (ProducesFlags.ProducesFlagsSideEffect (MInst.AluRRR (ALUOp.SbcS) (operand_size ty) (writable_zero_reg) src1 src2))) ;; Helper for emitting `MInst.BitRR` instructions. +(attr bit_rr (veri chain)) (decl bit_rr (BitOp Type Reg) Reg) (rule (bit_rr op ty src) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2311,6 +2699,7 @@ dst)) ;; Helper for emitting `adds` instructions. +(attr add_with_flags_paired (veri chain)) (decl add_with_flags_paired (Type Reg Reg) ProducesFlags) (rule (add_with_flags_paired ty src1 src2) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2319,6 +2708,7 @@ dst))) ;; Helper for emitting `adc` instructions. +(attr adc_paired (veri chain)) (decl adc_paired (Type Reg Reg) ConsumesFlags) (rule (adc_paired ty src1 src2) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2327,6 +2717,7 @@ dst))) ;; Helper for emitting `subs` instructions. +(attr sub_with_flags_paired (veri chain)) (decl sub_with_flags_paired (Type Reg Reg) ProducesFlags) (rule (sub_with_flags_paired ty src1 src2) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2334,49 +2725,33 @@ (MInst.AluRRR (ALUOp.SubS) (operand_size ty) dst src1 src2) dst))) +(attr cmn_imm (veri chain)) (decl cmn_imm (OperandSize Reg Imm12) ProducesFlags) (rule (cmn_imm size src1 src2) (ProducesFlags.ProducesFlagsSideEffect (MInst.AluRRImm12 (ALUOp.AddS) size (writable_zero_reg) src1 src2))) -(spec (cmp ty x y) - (provide (= result (subs ty x y))) - (require - (or (= ty 32) (= ty 64)))) +(attr cmp (veri chain)) (decl cmp (OperandSize Reg Reg) ProducesFlags) (rule (cmp size src1 src2) (ProducesFlags.ProducesFlagsSideEffect (MInst.AluRRR (ALUOp.SubS) size (writable_zero_reg) src1 src2))) -(spec (cmp_imm ty x y) - (provide (= result (subs ty x (zero_ext 64 y)))) - (require (or (= ty 32) (= ty 64)))) +(attr cmp_imm (veri chain)) (decl cmp_imm (OperandSize Reg Imm12) ProducesFlags) (rule (cmp_imm size src1 src2) (ProducesFlags.ProducesFlagsSideEffect (MInst.AluRRImm12 (ALUOp.SubS) size (writable_zero_reg) src1 src2))) +(attr cmp64_imm (veri chain)) (decl cmp64_imm (Reg Imm12) ProducesFlags) (rule (cmp64_imm src1 src2) (cmp_imm (OperandSize.Size64) src1 src2)) -(spec (cmp_extend ty x y extend) - (provide - (= result - (subs ty x - (switch extend - ((ExtendOp.UXTB) (zero_ext 64 (extract 7 0 y))) - ((ExtendOp.UXTH) (zero_ext 64 (extract 15 0 y))) - ((ExtendOp.UXTW) (zero_ext 64 (extract 31 0 y))) - ((ExtendOp.UXTX) (zero_ext 64 (extract 63 0 y))) - ((ExtendOp.SXTB) (sign_ext 64 (extract 7 0 y))) - ((ExtendOp.SXTH) (sign_ext 64 (extract 15 0 y))) - ((ExtendOp.SXTW) (sign_ext 64 (extract 31 0 y))) - ((ExtendOp.SXTX) (sign_ext 64 (extract 63 0 y))))))) - (require (or (= ty 32) (= ty 64)))) +(attr cmp_extend (veri chain)) (decl cmp_extend (OperandSize Reg Reg ExtendOp) ProducesFlags) (rule (cmp_extend size src1 src2 extend) (ProducesFlags.ProducesFlagsSideEffect @@ -2384,6 +2759,7 @@ src1 src2 extend))) ;; Helper for emitting `sbc` instructions. +(attr sbc_paired (veri chain)) (decl sbc_paired (Type Reg Reg) ConsumesFlags) (rule (sbc_paired ty src1 src2) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2477,7 +2853,9 @@ ;; ;; Recursion: may recurse once to downgrade from F16 to F32 when FP16 is not enabled. (decl rec fpu_csel (Type Cond Reg Reg) ConsumesFlags) -(rule (fpu_csel $F16 cond if_true if_false) + +(attr rule fpu_csel_f16 (tag narrowfloat)) +(rule fpu_csel_f16 (fpu_csel $F16 cond if_true if_false) (fpu_csel $F32 cond if_true if_false)) (rule 1 (fpu_csel $F16 cond if_true if_false) @@ -2508,6 +2886,7 @@ dst))) ;; Helper for emitting `MInst.FpuRound` instructions. +(attr fpu_round (veri chain)) (decl fpu_round (FpuRoundMode Reg) Reg) (rule (fpu_round op rn) (let ((dst WritableReg (temp_writable_reg $F64)) @@ -2526,20 +2905,26 @@ dst)) ;; Helper for emitting `MInst.MovToFpu` instructions. -;; -;; Recursion: may recurse once to downgrade from F16 to F32 when FP16 is not enabled. -(spec (mov_to_fpu x s) - (provide (= result (zero_ext 64 (conv_to s x))))) -(decl rec mov_to_fpu (Reg ScalarSize) Reg) +(attr mov_to_fpu (veri chain)) +(attr mov_to_fpu (veri chain)) +(decl mov_to_fpu (Reg ScalarSize) Reg) (rule (mov_to_fpu x size) - (let ((dst WritableReg (temp_writable_reg $I8X16)) - (_ Unit (emit (MInst.MovToFpu dst x size)))) + (let ((s ScalarSize (size_for_mov_to_fpu size)) + (dst WritableReg (temp_writable_reg $I8X16)) + (_ Unit (emit (MInst.MovToFpu dst x s)))) dst)) -(rule 1 (mov_to_fpu x (ScalarSize.Size16)) + +;; Helper to to downgrade mov_to_fpu from F16 to F32 when FP16 is not enabled. +(decl size_for_mov_to_fpu (ScalarSize) ScalarSize) +(rule (size_for_mov_to_fpu size) size) + +(attr rule mov_to_fpu_16 (tag narrowfloat)) +(rule mov_to_fpu_16 1 (size_for_mov_to_fpu (ScalarSize.Size16)) (if-let false (use_fp16)) - (mov_to_fpu x (ScalarSize.Size32))) + (ScalarSize.Size32)) ;; Helper for emitting `MInst.FpuMoveFPImm` instructions. +(attr fpu_move_fp_imm (veri chain)) (decl fpu_move_fp_imm (ASIMDFPModImm ScalarSize) Reg) (rule (fpu_move_fp_imm imm size) (let ((dst WritableReg (temp_writable_reg $I8X16)) @@ -2561,30 +2946,7 @@ dst)) ;; Helper for emitting `MInst.MovFromVec` instructions. -(spec (mov_from_vec x i s) - (provide - (= result - (switch s - (8 - (switch i - (#x00 (zero_ext 64 (extract 7 0 x))) - (#x01 (zero_ext 64 (extract 15 8 x))) - (#x02 (zero_ext 64 (extract 23 16 x))) - (#x03 (zero_ext 64 (extract 31 24 x))) - (#x04 (zero_ext 64 (extract 39 32 x))) - (#x05 (zero_ext 64 (extract 47 40 x))) - (#x06 (zero_ext 64 (extract 55 48 x))) - (#x07 (zero_ext 64 (extract 63 56 x))))) - (16 - (switch i - (#x00 (zero_ext 64 (extract 15 0 x))) - (#x01 (zero_ext 64 (extract 31 16 x))) - (#x03 (zero_ext 64 (extract 47 32 x))) - (#x04 (zero_ext 64 (extract 63 48 x))))) - (32 - (switch i - (#x00 (zero_ext 64 (extract 31 0 x))) - (#x01 (zero_ext 64 (extract 63 32 x))))))))) +(attr mov_from_vec (veri chain)) (decl mov_from_vec (Reg u8 ScalarSize) Reg) (rule (mov_from_vec rn idx size) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2605,11 +2967,7 @@ dst)) ;; Helper for emitting `MInst.Extend` instructions. -(spec (extend a b c d) - (provide - (if b - (= result (sign_ext (bv2int d) (conv_to (bv2int c) a))) - (= result (zero_ext (bv2int d) (conv_to (bv2int c) a)))))) +(attr extend (veri chain)) (decl extend (Reg bool u8 u8) Reg) (rule (extend rn signed from_bits to_bits) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -2617,6 +2975,7 @@ dst)) ;; Helper for emitting `MInst.FpuExtend` instructions. +(attr fpu_extend (veri chain)) (decl fpu_extend (Reg ScalarSize) Reg) (rule (fpu_extend src size) (let ((dst WritableReg (temp_writable_reg $F32X4)) @@ -2653,6 +3012,7 @@ ;; ;; Produces a `ProducesFlags` rather than a register or emitted instruction ;; which must be paired with `with_flags*` helpers. +(attr tst_imm (veri chain)) (decl tst_imm (Type Reg ImmLogic) ProducesFlags) (rule (tst_imm ty reg imm) (ProducesFlags.ProducesFlagsSideEffect @@ -2667,6 +3027,7 @@ ;; Note that this doesn't actually emit anything, instead it produces a ;; `ConsumesFlags` instruction which must be consumed with `with_flags*` ;; helpers. +(attr csel (veri chain)) (decl csel (Cond Reg Reg) ConsumesFlags) (rule (csel cond if_true if_false) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2675,6 +3036,7 @@ dst))) ;; Helper for constructing `cset` instructions. +(attr cset (veri chain)) (decl cset (Cond) ConsumesFlags) (rule (cset cond) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2682,6 +3044,7 @@ ;; Helper for constructing `cset` instructions, when the flags producer will ;; also return a value. +(attr cset_paired (veri chain)) (decl cset_paired (Cond) ConsumesFlags) (rule (cset_paired cond) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2698,6 +3061,7 @@ ;; Note that this doesn't actually emit anything, instead it produces a ;; `ConsumesFlags` instruction which must be consumed with `with_flags*` ;; helpers. +(attr csneg (veri chain)) (decl csneg (Cond Reg Reg) ConsumesFlags) (rule (csneg cond if_true if_false) (let ((dst WritableReg (temp_writable_reg $I64))) @@ -2722,71 +3086,23 @@ (value_reg dst)))) ;; Helpers for generating `add` instructions. -(spec (add ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) (extract 31 0 b))) - (bvadd a b))))) +(attr add (veri chain)) (decl add (Type Reg Reg) Reg) (rule (add ty x y) (alu_rrr (ALUOp.Add) ty x y)) -(spec (add_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) (zero_ext 32 b))) - (bvadd a (zero_ext 64 b))))) - (require - (or - (= b (bvand b #x000fff)) - (= b (bvand b #xfff000))))) +(attr add_imm (veri chain)) (decl add_imm (Type Reg Imm12) Reg) (rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) -(spec (add_extend ty x y) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 x) - (switch (extract 66 64 y) - ((ExtendOp.UXTB) (zero_ext 32 (extract 7 0 y))) - ((ExtendOp.UXTH) (zero_ext 32 (extract 15 0 y))) - ((ExtendOp.UXTW) (zero_ext 32 (extract 31 0 y))) - ((ExtendOp.UXTX) (zero_ext 32 (extract 31 0 y))) - ((ExtendOp.SXTB) (sign_ext 32 (extract 7 0 y))) - ((ExtendOp.SXTH) (sign_ext 32 (extract 15 0 y))) - ((ExtendOp.SXTW) (sign_ext 32 (extract 31 0 y))) - ((ExtendOp.SXTX) (sign_ext 32 (extract 31 0 y)))))) - (bvadd x - (switch (extract 66 64 y) - ((ExtendOp.UXTB) (zero_ext 64 (extract 7 0 y))) - ((ExtendOp.UXTH) (zero_ext 64 (extract 15 0 y))) - ((ExtendOp.UXTW) (zero_ext 64 (extract 31 0 y))) - ((ExtendOp.UXTX) (zero_ext 64 (extract 63 0 y))) - ((ExtendOp.SXTB) (sign_ext 64 (extract 7 0 y))) - ((ExtendOp.SXTH) (sign_ext 64 (extract 15 0 y))) - ((ExtendOp.SXTW) (sign_ext 64 (extract 31 0 y))) - ((ExtendOp.SXTX) (sign_ext 64 (extract 63 0 y))))))))) +(attr add_extend (veri chain)) (decl add_extend (Type Reg ExtendedValue) Reg) (rule (add_extend ty x y) (alu_rr_extend_reg (ALUOp.Add) ty x y)) +(attr add_extend_op (veri chain)) (decl add_extend_op (Type Reg Reg ExtendOp) Reg) (rule (add_extend_op ty x y extend) (alu_rrr_extend (ALUOp.Add) ty x y extend)) -(spec (add_shift ty a b shift) - (provide - (= result (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) - (switch (extract 15 8 shift) - ((ALUOp.Lsl) (bvshl (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsr) (bvlshr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Asr) (bvashr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))) - (bvadd a - (switch (extract 15 8 shift) - ((ALUOp.Lsl) (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsr) (bvlshr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Asr) (bvashr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))))) +(attr add_shift (veri chain)) (decl add_shift (Type Reg Reg ShiftOpAndAmt) Reg) (rule (add_shift ty x y z) (alu_rrr_shift (ALUOp.Add) ty x y z)) @@ -2794,72 +3110,26 @@ (rule (add_vec x y size) (vec_rrr (VecALUOp.Add) x y size)) ;; Helpers for generating `sub` instructions. -(spec (sub ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (extract 31 0 b))) - (bvsub a b))))) +(attr sub (veri chain)) (decl sub (Type Reg Reg) Reg) (rule (sub ty x y) (alu_rrr (ALUOp.Sub) ty x y)) -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require - (or - (= b (bvand b #x000fff)) - (= b (bvand b #xfff000))))) +(attr sub_imm (veri chain)) (decl sub_imm (Type Reg Imm12) Reg) (rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) -(spec (sub_extend ty x y) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 x) - (switch (extract 66 64 y) - ((ExtendOp.UXTB) (zero_ext 32 (extract 7 0 y))) - ((ExtendOp.UXTH) (zero_ext 32 (extract 15 0 y))) - ((ExtendOp.UXTW) (zero_ext 32 (extract 31 0 y))) - ((ExtendOp.UXTX) (zero_ext 32 (extract 31 0 y))) - ((ExtendOp.SXTB) (sign_ext 32 (extract 7 0 y))) - ((ExtendOp.SXTH) (sign_ext 32 (extract 15 0 y))) - ((ExtendOp.SXTW) (sign_ext 32 (extract 31 0 y))) - ((ExtendOp.SXTX) (sign_ext 32 (extract 31 0 y)))))) - (bvsub x - (switch (extract 66 64 y) - ((ExtendOp.UXTB) (zero_ext 64 (extract 7 0 y))) - ((ExtendOp.UXTH) (zero_ext 64 (extract 15 0 y))) - ((ExtendOp.UXTW) (zero_ext 64 (extract 31 0 y))) - ((ExtendOp.UXTX) (zero_ext 64 (extract 63 0 y))) - ((ExtendOp.SXTB) (sign_ext 64 (extract 7 0 y))) - ((ExtendOp.SXTH) (sign_ext 64 (extract 15 0 y))) - ((ExtendOp.SXTW) (sign_ext 64 (extract 31 0 y))) - ((ExtendOp.SXTX) (sign_ext 64 (extract 63 0 y))))))))) +(attr sub_extend (veri chain)) (decl sub_extend (Type Reg ExtendedValue) Reg) (rule (sub_extend ty x y) (alu_rr_extend_reg (ALUOp.Sub) ty x y)) -(spec (sub_shift ty a b shift) - (provide - (= result (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (switch (extract 15 8 shift) - ((ALUOp.Lsl) (bvshl (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsr) (bvlshr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Asr) (bvashr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))) - (bvsub a (switch (extract 15 8 shift) - ((ALUOp.Lsl) (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsr) (bvlshr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Asr) (bvashr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))))) +(attr sub_shift (veri chain)) (decl sub_shift (Type Reg Reg ShiftOpAndAmt) Reg) (rule (sub_shift ty x y z) (alu_rrr_shift (ALUOp.Sub) ty x y z)) (decl sub_vec (Reg Reg VectorSize) Reg) (rule (sub_vec x y size) (vec_rrr (VecALUOp.Sub) x y size)) +(attr sub_i128 (veri chain)) (decl sub_i128 (ValueRegs ValueRegs) ValueRegs) (rule (sub_i128 x y) (let @@ -2879,30 +3149,22 @@ (sbc_paired $I64 x_hi y_hi)))) ;; Helpers for generating `madd` instructions. -(spec (madd ty a b c) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b)))) - (bvadd c (bvmul a b)))))) +(attr madd (veri chain)) (decl madd (Type Reg Reg Reg) Reg) (rule (madd ty x y z) (alu_rrrr (ALUOp3.MAdd) ty x y z)) ;; Helpers for generating `msub` instructions. -(spec (msub ty a b c) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b)))) - (bvsub c (bvmul a b)))))) +(attr msub (veri chain)) (decl msub (Type Reg Reg Reg) Reg) (rule (msub ty x y z) (alu_rrrr (ALUOp3.MSub) ty x y z)) ;; Helpers for generating `umaddl` instructions +(attr umaddl (veri chain)) (decl umaddl (Reg Reg Reg) Reg) (rule (umaddl x y z) (alu_rrrr (ALUOp3.UMAddL) $I32 x y z)) ;; Helpers for generating `smaddl` instructions +(attr smaddl (veri chain)) (decl smaddl (Reg Reg Reg) Reg) (rule (smaddl x y z) (alu_rrrr (ALUOp3.SMAddL) $I32 x y z)) @@ -2923,10 +3185,12 @@ (rule (sqsub x y size) (vec_rrr (VecALUOp.Sqsub) x y size)) ;; Helper for generating `umulh` instructions. +(attr umulh (veri chain)) (decl umulh (Type Reg Reg) Reg) (rule (umulh ty x y) (alu_rrr (ALUOp.UMulH) ty x y)) ;; Helper for generating `smulh` instructions. +(attr smulh (veri chain)) (decl smulh (Type Reg Reg) Reg) (rule (smulh ty x y) (alu_rrr (ALUOp.SMulH) ty x y)) @@ -2998,28 +3262,7 @@ (SideEffectNoResult.Inst (MInst.Brk))) ;; Helper for generating `addp` instructions. -(spec (addp x y s) - (provide - (= result - (switch s - (#x00 (concat - (bvadd (extract 55 48 x) (extract 63 56 x)) - (bvadd (extract 39 32 x) (extract 47 40 x)) - (bvadd (extract 23 16 x) (extract 31 24 x)) - (bvadd (extract 7 0 x) (extract 15 8 x)) - (bvadd (extract 55 48 y) (extract 63 56 y)) - (bvadd (extract 39 32 y) (extract 47 40 y)) - (bvadd (extract 23 16 y) (extract 31 24 y)) - (bvadd (extract 7 0 y) (extract 15 8 y)))) - (#x01 (concat - (bvadd (extract 47 32 x) (extract 63 48 x)) - (bvadd (extract 15 0 x) (extract 31 16 x)) - (bvadd (extract 47 32 y) (extract 63 48 y)) - (bvadd (extract 15 0 y) (extract 31 16 y)))) - (#x02 (concat - (bvadd (extract 31 0 x) (extract 63 32 x)) - (bvadd (extract 31 0 y) (extract 63 32 y))))))) - (require (or (= s #x00) (= s #x01) (= s #x02)))) + (decl addp (Reg Reg VectorSize) Reg) (rule (addp x y size) (vec_rrr (VecALUOp.Addp) x y size)) @@ -3033,44 +3276,14 @@ ;; Helper for generating instruction sequences to calculate a scalar absolute ;; value. -(spec (abs s x) - (provide - (= result - (if (= s 32) - (conv_to 64 - (if (bvsge (extract 31 0 x) #x00000000) - (extract 31 0 x) - (bvneg (extract 31 0 x)))) - (if (bvsge x #x0000000000000000) x (bvneg x))))) - (require (or (= s 32) (= s 64)))) +(attr abs (veri chain)) (decl abs (OperandSize Reg) Reg) (rule (abs size x) (value_regs_get (with_flags (cmp_imm size x (u8_into_imm12 0)) (csneg (Cond.Gt) x x)) 0)) ;; Helper for generating `addv` instructions. -(spec (addv x s) - (provide - (= result - (switch s - (#x00 (zero_ext 64 - (bvadd (extract 7 0 x) - (bvadd (extract 15 8 x) - (bvadd (extract 23 16 x) - (bvadd (extract 31 24 x) - (bvadd (extract 39 32 x) - (bvadd (extract 47 40 x) - (bvadd (extract 55 48 x) - (extract 63 56 x)))))))))) - (#x01 (zero_ext 64 - (bvadd (extract 15 0 x) - (bvadd (extract 31 16 x) - (bvadd (extract 47 32 x) - (extract 63 48 x)))))) - (#x02 (zero_ext 64 - (bvadd (extract 31 0 x) - (extract 63 32 x))))))) - (require (or (= s #x00) (or (= s #x01) (= s #x02))))) + (decl addv (Reg VectorSize) Reg) (rule (addv x size) (vec_lanes (VecLanesOp.Addv) x size)) @@ -3121,67 +3334,39 @@ (rule (umull32 x y high_half) (vec_rrr_long (VecRRRLongOp.Umull32) x y high_half)) ;; Helper for generating `asr` instructions. +(attr asr (veri chain)) (decl asr (Type Reg Reg) Reg) (rule (asr ty x y) (alu_rrr (ALUOp.Asr) ty x y)) +(attr asr_imm (veri chain)) (decl asr_imm (Type Reg ImmShift) Reg) (rule (asr_imm ty x imm) (alu_rr_imm_shift (ALUOp.Asr) ty x imm)) ;; Helper for generating `lsr` instructions. -(spec (lsr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvlshr (extract 31 0 a) (extract 31 0 b)))) - (64 (bvlshr a b)))))) +(attr lsr (veri chain)) (decl lsr (Type Reg Reg) Reg) (rule (lsr ty x y) (alu_rrr (ALUOp.Lsr) ty x y)) -(spec (lsr_imm ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvlshr (extract 31 0 a) (zero_ext 32 b)))) - (64 (bvlshr a (zero_ext 64 b))))))) +(attr lsr_imm (veri chain)) (decl lsr_imm (Type Reg ImmShift) Reg) (rule (lsr_imm ty x imm) (alu_rr_imm_shift (ALUOp.Lsr) ty x imm)) ;; Helper for generating `lsl` instructions. -(spec (lsl ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvshl (extract 31 0 a) (extract 31 0 b)))) - (64 (bvshl a b)))))) +(attr lsl (veri chain)) (decl lsl (Type Reg Reg) Reg) (rule (lsl ty x y) (alu_rrr (ALUOp.Lsl) ty x y)) -(spec (lsl_imm ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvshl (extract 31 0 a) (zero_ext 32 b)))) - (64 (bvshl a (zero_ext 64 b))))))) +(attr lsl_imm (veri chain)) (decl lsl_imm (Type Reg ImmShift) Reg) (rule (lsl_imm ty x imm) (alu_rr_imm_shift (ALUOp.Lsl) ty x imm)) ;; Helper for generating `udiv` instructions. -(spec (a64_udiv ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvudiv (extract 31 0 a) (extract 31 0 b))) - (bvudiv a b))))) +(attr a64_udiv (veri chain)) (decl a64_udiv (Type Reg Reg) Reg) (rule (a64_udiv ty x y) (alu_rrr (ALUOp.UDiv) ty x y)) ;; Helper for generating `sdiv` instructions. -(spec (a64_sdiv ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsdiv (extract 31 0 a) (extract 31 0 b))) - (bvsdiv a b))))) +(attr a64_sdiv (veri chain)) (decl a64_sdiv (Type Reg Reg) Reg) (rule (a64_sdiv ty x y) (alu_rrr (ALUOp.SDiv) ty x y)) @@ -3190,47 +3375,24 @@ (rule (not x size) (vec_misc (VecMisc2.Not) x size)) ;; Helpers for generating `orr_not` instructions. -(spec (orr_not ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvor (extract 31 0 a) (bvnot (extract 31 0 b)))) - (bvor a (bvnot b)))))) +(attr orr_not (veri chain)) (decl orr_not (Type Reg Reg) Reg) (rule (orr_not ty x y) (alu_rrr (ALUOp.OrrNot) ty x y)) -(spec (orr_not_shift ty a b shift) - (provide - (= result (if (<= ty 32) - (conv_to 64 (bvor a (bvnot (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))) - (bvor a (bvnot (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))))) +(attr orr_not_shift (veri chain)) (decl orr_not_shift (Type Reg Reg ShiftOpAndAmt) Reg) (rule (orr_not_shift ty x y shift) (alu_rrr_shift (ALUOp.OrrNot) ty x y shift)) ;; Helpers for generating `orr` instructions. -(spec (orr ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvor (extract 31 0 a) (extract 31 0 b))) - (bvor a b)))) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) +(attr orr (veri chain)) (decl orr (Type Reg Reg) Reg) (rule (orr ty x y) (alu_rrr (ALUOp.Orr) ty x y)) -(spec (orr_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvor (extract 31 0 x) (extract 31 0 y)))) - (64 (bvor x (zero_ext 64 y)))))) - (require - (or - (= y (bvand y #x0000000000000fff)) - (= y (bvand y #x0000000000fff000))))) +(attr orr_imm (veri chain)) (decl orr_imm (Type Reg ImmLogic) Reg) (rule (orr_imm ty x y) (alu_rr_imm_logic (ALUOp.Orr) ty x y)) +(attr orr_shift (veri chain)) (decl orr_shift (Type Reg Reg ShiftOpAndAmt) Reg) (rule (orr_shift ty x y shift) (alu_rrr_shift (ALUOp.Orr) ty x y shift)) @@ -3241,26 +3403,11 @@ (rule (orn_vec x y size) (vec_rrr (VecALUOp.Orn) x y size)) ;; Helpers for generating `and` instructions. -(spec (and_reg ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvand (extract 31 0 a) (extract 31 0 b))) - (bvand a b)))) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) +(attr and_reg (veri chain)) (decl and_reg (Type Reg Reg) Reg) (rule (and_reg ty x y) (alu_rrr (ALUOp.And) ty x y)) -(spec (and_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvand (extract 31 0 x) (extract 31 0 y)))) - (64 (bvand x (zero_ext 64 y)))))) - (require - (or - (= y (bvand y #x0000000000000fff)) - (= y (bvand y #x0000000000fff000))))) +(attr and_imm (veri chain)) (decl and_imm (Type Reg ImmLogic) Reg) (rule (and_imm ty x y) (alu_rr_imm_logic (ALUOp.And) ty x y)) @@ -3268,6 +3415,7 @@ (rule (and_vec x y size) (vec_rrr (VecALUOp.And) x y size)) ;; Helpers for generating `eor` instructions. +(attr eor (veri chain)) (decl eor (Type Reg Reg) Reg) (rule (eor ty x y) (alu_rrr (ALUOp.Eor) ty x y)) @@ -3275,15 +3423,7 @@ (rule (eor_vec x y size) (vec_rrr (VecALUOp.Eor) x y size)) ;; Helpers for generating `bic` instructions. -(spec (bic ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvand (extract 31 0 a) (bvnot (extract 31 0 b)))) - (bvand a (bvnot b)) - ) - )) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) +(attr bic (veri chain)) (decl bic (Type Reg Reg) Reg) (rule (bic ty x y) (alu_rrr (ALUOp.AndNot) ty x y)) @@ -3314,100 +3454,74 @@ ;; ;; Note that the `Extr` opcode is used here as `rotr` is an alias for that ;; instruction where two operands are the same register. -(spec (a64_rotr ty x y) - (provide - (= result - (if (= ty 32) - (zero_ext 64 (rotr (extract 31 0 x) (extract 31 0 y))) - (rotr x y)))) - (require (or (= ty 32) (= ty 64)))) +(attr a64_rotr (veri chain)) (decl a64_rotr (Type Reg Reg) Reg) (rule (a64_rotr ty x y) (alu_rrr (ALUOp.Extr) ty x y)) -(spec (a64_rotr_imm ty x y) - (provide - (= result - (if (= ty 32) - (zero_ext 64 (rotr (extract 31 0 x) (zero_ext 32 y))) - (rotr x (zero_ext 64 y))))) - (require (or (= ty 32) (= ty 64)))) +(attr a64_rotr_imm (veri chain)) (decl a64_rotr_imm (Type Reg ImmShift) Reg) (rule (a64_rotr_imm ty x y) (alu_rr_imm_shift (ALUOp.Extr) ty x y)) ;; Helpers for generating `extr` instructions +(attr a64_extr (veri chain)) (decl a64_extr (Type Reg Reg ImmShift) Reg) (rule (a64_extr ty x y shift) (alu_rrr_shift (ALUOp.Extr) ty x y (a64_extr_imm ty shift))) + +(spec (a64_extr_imm ty shift) + (require + ; Type must be either I32 or I64. + (or (= (:bits ty) 32) (= (:bits ty) 64)) + ) + (provide + ; The op field encodes the type: I32 (LSL), I64 (LSR). + (= (:op result) + (switch (:bits ty) + (32 (ALUOp.Lsl)) + (64 (ALUOp.Lsr)) + ) + ) + ; The amt field is the shift value, zero-extended from 6 to 8 bits. + (= (:amt result) (zero_ext 8 shift)) + ) +) (decl a64_extr_imm (Type ImmShift) ShiftOpAndAmt) (extern constructor a64_extr_imm a64_extr_imm) ;; Helpers for generating `rbit` instructions. -(spec (rbit ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (rev (extract 31 0 a))) - (rev a)))) - (require (or (= ty 32) (= ty 64)))) +(attr rbit (veri chain)) (decl rbit (Type Reg) Reg) (rule (rbit ty x) (bit_rr (BitOp.RBit) ty x)) ;; Helpers for generating `clz` instructions. -(spec (a64_clz ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (clz (extract 31 0 a))) - (clz a)))) - (require (or (= ty 32) (= ty 64)))) +(attr a64_clz (veri chain)) (decl a64_clz (Type Reg) Reg) (rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x)) ;; Helpers for generating `cls` instructions. -(spec (a64_cls ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (cls (extract 31 0 a))) - (cls a)))) - (require (or (= ty 32) (= ty 64)))) +(attr a64_cls (veri chain)) (decl a64_cls (Type Reg) Reg) (rule (a64_cls ty x) (bit_rr (BitOp.Cls) ty x)) ;; Helpers for generating `rev` instructions - +(attr a64_rev16 (veri chain)) (decl a64_rev16 (Type Reg) Reg) (rule (a64_rev16 ty x) (bit_rr (BitOp.Rev16) ty x)) +(attr a64_rev32 (veri chain)) (decl a64_rev32 (Type Reg) Reg) (rule (a64_rev32 ty x) (bit_rr (BitOp.Rev32) ty x)) +(attr a64_rev64 (veri chain)) (decl a64_rev64 (Type Reg) Reg) (rule (a64_rev64 ty x) (bit_rr (BitOp.Rev64) ty x)) ;; Helpers for generating `eon` instructions. - +(attr eon (veri chain)) (decl eon (Type Reg Reg) Reg) (rule (eon ty x y) (alu_rrr (ALUOp.EorNot) ty x y)) ;; Helpers for generating `cnt` instructions. -(spec (vec_cnt x s) - (provide - (= result - (switch s - ((VectorSize.Size8x8) - (concat - (popcnt (extract 63 56 x)) - (popcnt (extract 55 48 x)) - (popcnt (extract 47 40 x)) - (popcnt (extract 39 32 x)) - (popcnt (extract 31 24 x)) - (popcnt (extract 23 16 x)) - (popcnt (extract 15 8 x)) - (popcnt (extract 7 0 x)))) - ((VectorSize.Size16x4) result) - ((VectorSize.Size32x2) result)))) - (require - (or (= s (VectorSize.Size8x8)) (= s (VectorSize.Size16x4)) (= s (VectorSize.Size32x2))))) + (decl vec_cnt (Reg VectorSize) Reg) (rule (vec_cnt x size) (vec_misc (VecMisc2.Cnt) x size)) @@ -3425,49 +3539,52 @@ ;; Helpers for generating various load instructions, with varying ;; widths and sign/zero-extending properties. +(attr aarch64_uload8 (veri chain)) (decl aarch64_uload8 (AMode MemFlagsData) Reg) -(spec (aarch64_uload8 amode flags) - (provide (= result (zero_ext 32 (load_effect flags 8 amode)))) - (require (= 32 (widthof result)))) (rule (aarch64_uload8 amode flags) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.ULoad8 dst amode flags)))) dst)) + +(attr aarch64_sload8 (veri chain)) (decl aarch64_sload8 (AMode MemFlagsData) Reg) (rule (aarch64_sload8 amode flags) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.SLoad8 dst amode flags)))) dst)) + +(attr aarch64_uload16 (veri chain)) (decl aarch64_uload16 (AMode MemFlagsData) Reg) -(spec (aarch64_uload16 amode flags) - (provide (= result (zero_ext 32 (load_effect flags 16 amode)))) - (require (= 32 (widthof result)))) (rule (aarch64_uload16 amode flags) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.ULoad16 dst amode flags)))) dst)) + +(attr aarch64_sload16 (veri chain)) (decl aarch64_sload16 (AMode MemFlagsData) Reg) (rule (aarch64_sload16 amode flags) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.SLoad16 dst amode flags)))) dst)) + +(attr aarch64_uload32 (veri chain)) (decl aarch64_uload32 (AMode MemFlagsData) Reg) -(spec (aarch64_uload32 amode flags) - (provide (= result (load_effect flags 32 amode))) - (require (= 32 (widthof result)))) + (rule (aarch64_uload32 amode flags) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.ULoad32 dst amode flags)))) dst)) + +(attr aarch64_sload32 (veri chain)) (decl aarch64_sload32 (AMode MemFlagsData) Reg) (rule (aarch64_sload32 amode flags) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.SLoad32 dst amode flags)))) dst)) + +(attr aarch64_uload64 (veri chain)) (decl aarch64_uload64 (AMode MemFlagsData) Reg) -(spec (aarch64_uload64 amode flags) - (provide (= result (load_effect flags 64 amode))) - (require (= 64 (widthof result)))) + (rule (aarch64_uload64 amode flags) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.ULoad64 dst amode flags)))) @@ -3477,11 +3594,15 @@ (let ((dst WritableReg (temp_writable_reg $F64)) (_ Unit (emit (MInst.FpuLoad16 dst amode flags)))) dst)) + +(attr aarch64_fpuload32 (veri chain)) (decl aarch64_fpuload32 (AMode MemFlagsData) Reg) (rule (aarch64_fpuload32 amode flags) (let ((dst WritableReg (temp_writable_reg $F64)) (_ Unit (emit (MInst.FpuLoad32 dst amode flags)))) dst)) + +(attr aarch64_fpuload64 (veri chain)) (decl aarch64_fpuload64 (AMode MemFlagsData) Reg) (rule (aarch64_fpuload64 amode flags) (let ((dst WritableReg (temp_writable_reg $F64)) @@ -3501,24 +3622,27 @@ ;; Helpers for generating various store instructions with varying ;; widths. +(attr aarch64_store8 (veri chain)) (decl aarch64_store8 (AMode MemFlagsData Reg) SideEffectNoResult) -(spec (aarch64_store8 amode flags val) - (provide (= result (store_effect flags 8 (extract 7 0 val) amode)))) + (rule (aarch64_store8 amode flags val) (SideEffectNoResult.Inst (MInst.Store8 val amode flags))) + +(attr aarch64_store16 (veri chain)) (decl aarch64_store16 (AMode MemFlagsData Reg) SideEffectNoResult) -(spec (aarch64_store16 amode flags val) - (provide (= result (store_effect flags 16 (extract 15 0 val) amode)))) + (rule (aarch64_store16 amode flags val) (SideEffectNoResult.Inst (MInst.Store16 val amode flags))) + +(attr aarch64_store32 (veri chain)) (decl aarch64_store32 (AMode MemFlagsData Reg) SideEffectNoResult) -(spec (aarch64_store32 amode flags val) - (provide (= result (store_effect flags 32 (extract 31 0 val) amode)))) + (rule (aarch64_store32 amode flags val) (SideEffectNoResult.Inst (MInst.Store32 val amode flags))) + +(attr aarch64_store64 (veri chain)) (decl aarch64_store64 (AMode MemFlagsData Reg) SideEffectNoResult) -(spec (aarch64_store64 amode flags val) - (provide (= result (store_effect flags 64 val amode)))) + (rule (aarch64_store64 amode flags val) (SideEffectNoResult.Inst (MInst.Store64 val amode flags))) (decl aarch64_fpustore16 (AMode MemFlagsData Reg) SideEffectNoResult) @@ -3527,6 +3651,8 @@ (decl aarch64_fpustore32 (AMode MemFlagsData Reg) SideEffectNoResult) (rule (aarch64_fpustore32 amode flags val) (SideEffectNoResult.Inst (MInst.FpuStore32 val amode flags))) + +(attr aarch64_fpustore64 (veri chain)) (decl aarch64_fpustore64 (AMode MemFlagsData Reg) SideEffectNoResult) (rule (aarch64_fpustore64 amode flags val) (SideEffectNoResult.Inst (MInst.FpuStore64 val amode flags))) @@ -3534,16 +3660,19 @@ (rule (aarch64_fpustore128 amode flags val) (SideEffectNoResult.Inst (MInst.FpuStore128 val amode flags))) (decl aarch64_storep64 (PairAMode MemFlagsData Reg Reg) SideEffectNoResult) -(rule (aarch64_storep64 amode flags val1 val2) + +(attr rule lower_aarch64_storep64 (tag i128)) +(rule lower_aarch64_storep64 (aarch64_storep64 amode flags val1 val2) (SideEffectNoResult.Inst (MInst.StoreP64 val1 val2 amode flags))) ;; Helper for generating a `trapif` instruction. - +(attr trap_if_cond (veri chain)) (decl trap_if_cond (Cond TrapCode) ConsumesFlags) (rule (trap_if_cond cond trap_code) (ConsumesFlags.ConsumesFlagsSideEffect (MInst.TrapIf (cond_br_cond cond) trap_code))) +(attr trap_if_zero (veri chain)) (decl trap_if_zero (Reg OperandSize TrapCode) SideEffectNoResult) (rule (trap_if_zero reg size trap_code) (SideEffectNoResult.Inst @@ -3557,10 +3686,7 @@ ;; Immediate value helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Type of extension performed by an immediate helper -(model ImmExtend - (enum - (Sign #b0) - (Zero #b1))) + (type ImmExtend (enum (Sign) @@ -3576,21 +3702,7 @@ ;; Note that, unlike the convention in the AArch64 backend, this helper leaves ;; all bits in the destination register in a defined state, i.e. smaller types ;; such as `I8` are either sign- or zero-extended. -(spec (imm ty ext x) - (provide - (= result - (switch ty - (8 (if (= ext #b1) (zero_ext 64 (extract 7 0 x)) (sign_ext 64 (extract 7 0 x)))) - (16 (if (= ext #b1) (zero_ext 64 (extract 15 0 x)) (sign_ext 64 (extract 15 0 x)))) - (32 (if (= ext #b1) (zero_ext 64 (extract 32 0 x)) (sign_ext 64 (extract 32 0 x)))) - (64 x)))) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) -(instantiate imm - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 8))) - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 16))) - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 32))) - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 64))) -) +(attr imm (veri chain)) (decl imm (Type ImmExtend u64) Reg) ;; Move wide immediate instructions; to simplify, we only match when we @@ -3611,6 +3723,32 @@ (decl load_constant_full (Type ImmExtend OperandSize u64) Reg) (extern constructor load_constant_full load_constant_full) +;; The implementation extracts the low `(:bits ty)` bits of `const` and +;; extends them (sign- or zero-, per `imm`) into the destination register. +;; For Size32 destinations the upper 32 bits are zero-padded as usual on +;; AArch64; for Size64 the value occupies the full 64-bit register. +(spec (load_constant_full ty imm size const) + (provide + (=> (and (Size32? size) (= (:bits ty) 8)) + (= result + (match imm + ((Sign) (zero_ext 64 (sign_ext 32 (extract 7 0 const)))) + ((Zero) (zero_ext 64 (extract 7 0 const)))))) + (=> (and (Size32? size) (= (:bits ty) 16)) + (= result + (match imm + ((Sign) (zero_ext 64 (sign_ext 32 (extract 15 0 const)))) + ((Zero) (zero_ext 64 (extract 15 0 const)))))) + (=> (and (Size32? size) (= (:bits ty) 32)) + (= result (zero_ext 64 (extract 31 0 const)))) + (=> (and (Size64? size) (= (:bits ty) 64)) + (= result const))) + (require + (or + (and (Size32? size) (= (:bits ty) 8)) + (and (Size32? size) (= (:bits ty) 16)) + (and (Size32? size) (= (:bits ty) 32)) + (and (Size64? size) (= (:bits ty) 64))))) ;; Fallback for integral 32-bit constants (rule (imm (fits_in_32 (integral_ty ty)) extend n) @@ -3620,16 +3758,11 @@ (rule -1 (imm (integral_ty $I64) extend n) (load_constant_full $I64 extend (operand_size $I64) n)) - ;; Sign extension helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Place a `Value` into a register, sign extending it to 32-bits -(spec (put_in_reg_sext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (sign_ext 32 arg)) - (conv_to 64 arg))))) +(attr put_in_reg_sext32 (veri chain)) +(attr put_in_reg_sext32 (veri chain)) (decl put_in_reg_sext32 (Value) Reg) (rule -1 (put_in_reg_sext32 val @ (value_type (fits_in_32 ty))) (extend val true (ty_bits ty) 32)) @@ -3639,12 +3772,7 @@ (rule (put_in_reg_sext32 val @ (value_type $I64)) val) ;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) +(attr put_in_reg_zext32 (veri chain)) (decl put_in_reg_zext32 (Value) Reg) (rule -1 (put_in_reg_zext32 val @ (value_type (fits_in_32 ty))) (extend val false (ty_bits ty) 32)) @@ -3654,8 +3782,7 @@ (rule (put_in_reg_zext32 val @ (value_type $I64)) val) ;; Place a `Value` into a register, sign extending it to 64-bits -(spec (put_in_reg_sext64 x) - (provide (= (sign_ext 64 x) result))) +(attr put_in_reg_sext64 (veri chain)) (decl put_in_reg_sext64 (Value) Reg) (rule 1 (put_in_reg_sext64 val @ (value_type (fits_in_32 ty))) (extend val true (ty_bits ty) 64)) @@ -3664,8 +3791,8 @@ (rule (put_in_reg_sext64 val @ (value_type $I64)) val) ;; Place a `Value` into a register, zero extending it to 64-bits -(spec (put_in_reg_zext64 x) - (provide (= result (zero_ext 64 x)))) +(attr put_in_reg_zext64 (veri chain)) +(attr put_in_reg_zext64 (veri chain)) (decl put_in_reg_zext64 (Value) Reg) (rule 1 (put_in_reg_zext64 val @ (value_type (fits_in_32 ty))) (extend val false (ty_bits ty) 64)) @@ -3674,12 +3801,21 @@ (rule (put_in_reg_zext64 val @ (value_type $I64)) val) ;; Misc instruction helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - +(attr trap_if_zero_divisor (veri chain)) (decl trap_if_zero_divisor (Reg OperandSize) Reg) (rule (trap_if_zero_divisor reg size) (let ((_ Unit (emit (MInst.TrapIf (cond_br_zero reg size ) (trap_code_division_by_zero))))) reg)) +(spec (size_from_ty ty) + (provide + (= result (if (<= (:bits ty) 32) (OperandSize.Size32) (OperandSize.Size64)))) + (require + (or + (= (:bits ty) 8) + (= (:bits ty) 16) + (= (:bits ty) 32) + (= (:bits ty) 64)))) (decl size_from_ty (Type) OperandSize) (rule 1 (size_from_ty (fits_in_32 _ty)) (OperandSize.Size32)) (rule (size_from_ty $I64) (OperandSize.Size64)) @@ -3688,6 +3824,7 @@ ;; The following checks must be done in 32-bit or 64-bit, depending ;; on the input type. For 8- and 16- bit, the check for x == min_value ;; must use a possibly-shifted value, xcheck, to overflow as expected. +(attr trap_if_div_overflow (veri chain)) (decl trap_if_div_overflow (Type Reg Reg Reg) Reg) (rule (trap_if_div_overflow ty xcheck x y) (let ( @@ -3712,7 +3849,10 @@ ;; x by (32 - ty), we then produce the 32-bit min_value for the respective min ;; values of I8 and I16. ;; E.g., I8's 0x00000080 left-shifted by 24 is 0x80000000, which overflows. +(attr intmin_check (veri chain)) (decl intmin_check (Type Reg) Reg) + +(attr rule intmin_check_fits_in_16 (veri priority)) (rule intmin_check_fits_in_16 (intmin_check (fits_in_16 ty) x) (alu_rr_imm_shift (ALUOp.Lsl) ty x (imm_shift_from_u8 (diff_from_32 ty)))) @@ -3720,6 +3860,7 @@ (rule -1 (intmin_check ty x) x) ;; Check for unsigned overflow. +(attr trap_if_overflow (veri chain)) (decl trap_if_overflow (ProducesFlags TrapCode) Reg) (rule (trap_if_overflow producer tc) (with_flags_reg @@ -3727,6 +3868,7 @@ (ConsumesFlags.ConsumesFlagsSideEffect (MInst.TrapIf (cond_br_cond (Cond.Hs)) tc)))) +(attr sink_atomic_load (veri chain)) (decl sink_atomic_load (Inst) Reg) (rule (sink_atomic_load x @ (atomic_load _ _ addr)) (let ((_ Unit (sink_inst x))) @@ -3735,14 +3877,7 @@ ;; Helper for generating either an `AluRRR`, `AluRRRShift`, or `AluRRImmLogic` ;; instruction depending on the input. Note that this requires that the `ALUOp` ;; specified is commutative. -(spec (alu_rs_imm_logic_commutative op t a b) - (provide - (= result - (conv_to 64 - (switch op - ((ALUOp.Orr) (bvor a b)) - ((ALUOp.And) (bvand a b)) - ((ALUOp.Eor) (bvxor a b))))))) +(attr alu_rs_imm_logic_commutative (veri chain)) (decl alu_rs_imm_logic_commutative (ALUOp Type Value Value) Reg) ;; Base case of operating on registers. @@ -3767,14 +3902,7 @@ ;; Same as `alu_rs_imm_logic_commutative` above, except that it doesn't require ;; that the operation is commutative. -(spec (alu_rs_imm_logic op t a b) - (provide - (= result - (conv_to 64 - (switch op - ((ALUOp.OrrNot) (bvor a (bvnot b))) - ((ALUOp.EorNot) (bvxor a (bvnot b))) - ((ALUOp.AndNot) (bvand a (bvnot b)))))))) +(attr alu_rs_imm_logic (veri chain)) (decl alu_rs_imm_logic (ALUOp Type Value Value) Reg) (rule -1 (alu_rs_imm_logic op ty x y) (alu_rrr op ty x y)) @@ -3814,6 +3942,7 @@ (extern constructor is_pic is_pic) ;; Helper loading an external name into a register via `MInst.LoadExt*` +(attr load_ext_name (tag TODO)) (decl load_ext_name (BoxExternalName i64 RelocDistance) Reg) ;; When `is_pic` is true all names are referenced through the GOT. Note that @@ -3871,10 +4000,8 @@ ;; various modes are generated. This in theory would not be necessary with ;; mid-end optimizations that fold constants into load/store immediate offsets ;; instead, but for now each backend needs to do this. +(attr amode (veri chain)) (decl amode (Type Value i32) AMode) -(spec (amode ty val offset) - (provide (= result (bvadd val (sign_ext 64 offset)))) - (require (= 64 (widthof val)))) (rule 0 (amode ty val offset) (amode_no_more_iconst ty val offset)) @@ -3890,6 +4017,7 @@ (AMode.SlotOffset (abi_stackslot_offset_into_slot_region slot offset1 offset2))) +(attr amode_no_more_iconst (veri chain)) (decl amode_no_more_iconst (Type Value i32) AMode) ;; Base case: move the `offset` into a register and add it to `val` via the ;; amode @@ -3937,6 +4065,7 @@ (if-let true (u64_eq (ty_bytes ty) (u64_wrapping_shl 1 (shift_masked_imm shift_ty n)))) (amode_reg_scaled (amode_add x offset) y)) +(attr amode_reg_scaled (veri chain)) (decl amode_reg_scaled (Reg Value) AMode) (rule 0 (amode_reg_scaled base index) (AMode.RegScaled base index)) @@ -3947,6 +4076,7 @@ ;; Helper to add a 32-bit signed immediate to the register provided. This will ;; select an appropriate `add` instruction to use. +(attr amode_add (veri chain)) (decl amode_add (Reg i32) Reg) (rule 0 (amode_add x y) (add $I64 x (imm $I64 (ImmExtend.Zero) (i64_cast_unsigned y)))) @@ -3957,6 +4087,7 @@ ;; Creates a `PairAMode` for the `Value` provided plus the `i32` constant ;; offset provided. +(attr pair_amode (veri chain)) (decl pair_amode (Value i32) PairAMode) ;; Base case where `val` and `offset` are combined with an `add` @@ -3972,18 +4103,46 @@ (decl pure partial simm7_scaled_from_i64 (i64 Type) SImm7Scaled) (extern constructor simm7_scaled_from_i64 simm7_scaled_from_i64) +(spec (uimm12_scaled_from_i64 value ty) + (match + (let + ( + (scale (bits2bytes! (int2bv 64 (:bits ty)))) + (limit (bvmul (int2bv 64 4095) scale)) + ) + (and + (bvsge value (bvzero! 64)) + (bvsle value limit) + (bv_is_zero! (bvand value (bvsub scale (bvone! 64)))) + ) + ) + ) + (provide + (= result (extract 11 0 (bvudiv value (bits2bytes! (int2bv 64 (:bits ty)))))) + ) +) (decl pure partial uimm12_scaled_from_i64 (i64 Type) UImm12Scaled) (extern constructor uimm12_scaled_from_i64 uimm12_scaled_from_i64) +(spec (simm9_from_i64 value) + (provide (= value (sign_ext 64 result))) + (match + ; value >= -256 + (bvsge value (bvneg (int2bv 64 256))) + ; value <= 255 + (bvsle value (int2bv 64 255)) + ) +) (decl pure partial simm9_from_i64 (i64) SImm9) (extern constructor simm9_from_i64 simm9_from_i64) - +(attr sink_load_into_addr (veri chain)) (decl sink_load_into_addr (Type Inst) Reg) (rule (sink_load_into_addr ty x @ (load _ _ addr (offset32 offset))) (let ((_ Unit (sink_inst x))) (add_imm_to_addr addr (i64_cast_unsigned offset)))) +(attr add_imm_to_addr (veri chain)) (decl add_imm_to_addr (Reg u64) Reg) (rule 2 (add_imm_to_addr val 0) val) (rule 1 (add_imm_to_addr val (imm12_from_u64 imm)) (add_imm $I64 val imm)) @@ -4038,6 +4197,7 @@ ;; in their most significant bits). ;; TODO: Treat as half of a 128 bit vector and consider replicated patterns. ;; Scalar MOVI might also be an option. +(attr constant_f64 (veri chain)) (decl constant_f64 (u64) Reg) (rule 4 (constant_f64 0) (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size32)) @@ -4054,6 +4214,10 @@ (fpu_load64 (AMode.Const (emit_u64_le_const n)) (mem_flags_trusted_data))) ;; Tests whether the low 32 bits in the input are all zero. +(spec (u64_low32_bits_unset n) + (match (bv_is_zero! (bvand result #x00000000ffffffff))) + (provide (= n result)) +) (decl u64_low32_bits_unset (u64) u64) (extern extractor u64_low32_bits_unset u64_low32_bits_unset) @@ -4120,6 +4284,7 @@ (if-let imm (asimd_fp_mod_imm_from_u64 n (vector_lane_size size))) (vec_dup_fp_imm imm size)) +(attr vec_dup_fp_imm_supports_lane_size (veri chain)) (decl pure vec_dup_fp_imm_supports_lane_size (ScalarSize) bool) (rule 1 (vec_dup_fp_imm_supports_lane_size (ScalarSize.Size32)) true) (rule 1 (vec_dup_fp_imm_supports_lane_size (ScalarSize.Size64)) true) @@ -4131,7 +4296,24 @@ (vec_dup (imm $I64 (ImmExtend.Zero) n) size)) ;; Lower an integer cond code. -(spec (cond_code a) (provide (= a result))) +(spec (cond_code cc) + (provide + (= result + (match cc + ((Equal) (Cond.Eq)) + ((NotEqual) (Cond.Ne)) + ((SignedGreaterThanOrEqual) (Cond.Ge)) + ((SignedGreaterThan) (Cond.Gt)) + ((SignedLessThanOrEqual) (Cond.Le)) + ((SignedLessThan) (Cond.Lt)) + ((UnsignedGreaterThanOrEqual) (Cond.Hs)) + ((UnsignedGreaterThan) (Cond.Hi)) + ((UnsignedLessThanOrEqual) (Cond.Ls)) + ((UnsignedLessThan) (Cond.Lo)) + ) + ) + ) +) (decl cond_code (IntCC) Cond) ;; TODO: Port lower_condcode() to ISLE. (extern constructor cond_code cond_code) @@ -4140,6 +4322,30 @@ (decl invert_cond (Cond) Cond) ;; TODO: Port cond.invert() to ISLE. (extern constructor invert_cond invert_cond) +(spec (invert_cond cond) + (provide + (= result + (match cond + ((Eq) (Cond.Ne)) + ((Ne) (Cond.Eq)) + ((Hs) (Cond.Lo)) + ((Lo) (Cond.Hs)) + ((Mi) (Cond.Pl)) + ((Pl) (Cond.Mi)) + ((Vs) (Cond.Vc)) + ((Vc) (Cond.Vs)) + ((Hi) (Cond.Ls)) + ((Ls) (Cond.Hi)) + ((Ge) (Cond.Lt)) + ((Lt) (Cond.Ge)) + ((Gt) (Cond.Le)) + ((Le) (Cond.Gt)) + ((Al) (Cond.Nv)) + ((Nv) (Cond.Al)) + ) + ) + ) +) ;; Generate comparison to zero operator from input condition code (decl float_cc_cmp_zero_to_vec_misc_op (FloatCC) VecMisc2) @@ -4248,25 +4454,30 @@ dst)) ;; Helper for emitting `MInst.MovPReg` instructions. +(attr mov_from_preg (veri chain)) (decl mov_from_preg (PReg) Reg) (rule (mov_from_preg src) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.MovFromPReg dst src)))) dst)) +(attr mov_to_preg (veri chain)) (decl mov_to_preg (PReg Reg) SideEffectNoResult) (rule (mov_to_preg dst src) (SideEffectNoResult.Inst (MInst.MovToPReg dst src))) +(attr preg_sp (tag TODO)) (decl preg_sp () PReg) (extern constructor preg_sp preg_sp) +(attr preg_fp (tag TODO)) (decl preg_fp () PReg) (extern constructor preg_fp preg_fp) (decl preg_link () PReg) (extern constructor preg_link preg_link) +(attr preg_pinned (tag TODO)) (decl preg_pinned () PReg) (extern constructor preg_pinned preg_pinned) @@ -4308,27 +4519,29 @@ (mov_from_preg (preg_link)))) ;; Helper for getting the maximum shift amount for a type. - +(attr max_shift (veri chain)) (decl max_shift (Type) u8) (rule (max_shift $F64) 63) (rule (max_shift $F32) 31) ;; Helper for generating `fcopysign` instruction sequences. - +(attr fcopy_sign (veri chain)) (decl fcopy_sign (Reg Reg Type) Reg) (rule 1 (fcopy_sign x y (ty_scalar_float ty)) (let ((dst WritableReg (temp_writable_reg $F64)) (tmp Reg (fpu_rri (fpu_op_ri_ushr (ty_bits ty) (max_shift ty)) y)) (_ Unit (emit (MInst.FpuRRIMod (fpu_op_ri_sli (ty_bits ty) (max_shift ty)) dst x tmp)))) dst)) -(rule (fcopy_sign x y ty @ (multi_lane _ _)) + +(attr rule fcopy_sign_vec (tag vector)) +(rule fcopy_sign_vec (fcopy_sign x y ty @ (multi_lane _ _)) (let ((dst WritableReg (temp_writable_reg $I8X16)) (tmp Reg (ushr_vec_imm y (max_shift (lane_type ty)) (vector_size ty))) (_ Unit (emit (MInst.VecShiftImmMod (VecShiftImmModOp.Sli) dst x tmp (vector_size ty) (max_shift (lane_type ty)))))) dst)) ;; Helpers for generating `MInst.FpuToInt` instructions. - +(attr fpu_to_int_nan_check (veri chain)) (decl fpu_to_int_nan_check (ScalarSize Reg) Reg) (rule (fpu_to_int_nan_check size src) (let ((r ValueRegs @@ -4342,8 +4555,11 @@ ;; Checks that the value is not less than the minimum bound, ;; accepting a boolean (whether the type is signed), input type, ;; output type, and registers containing the source and minimum bound. +(attr fpu_to_int_underflow_check (veri chain)) (decl fpu_to_int_underflow_check (bool Type Type Reg Reg) Reg) -(rule (fpu_to_int_underflow_check true $F32 (fits_in_16 out_ty) src min) + +(attr rule fpu_to_int_underflow_check_signed_f32_fits_in_16 (veri priority)) +(rule fpu_to_int_underflow_check_signed_f32_fits_in_16 (fpu_to_int_underflow_check true $F32 (fits_in_16 out_ty) src min) (let ((r ValueRegs (with_flags (fpu_cmp (ScalarSize.Size32) src min) (ConsumesFlags.ConsumesFlagsReturnsReg @@ -4351,7 +4567,9 @@ (trap_code_integer_overflow)) src)))) (value_regs_get r 0))) -(rule (fpu_to_int_underflow_check true $F64 (fits_in_32 out_ty) src min) + +(attr rule fpu_to_int_underflow_check_signed_f64_fits_in_32 (veri priority)) +(rule fpu_to_int_underflow_check_signed_f64_fits_in_32 (fpu_to_int_underflow_check true $F64 (fits_in_32 out_ty) src min) (let ((r ValueRegs (with_flags (fpu_cmp (ScalarSize.Size64) src min) (ConsumesFlags.ConsumesFlagsReturnsReg @@ -4376,6 +4594,7 @@ src)))) (value_regs_get r 0))) +(attr fpu_to_int_overflow_check (veri chain)) (decl fpu_to_int_overflow_check (ScalarSize Reg Reg) Reg) (rule (fpu_to_int_overflow_check size src max) (let ((r ValueRegs @@ -4392,6 +4611,7 @@ ;; Accepts the specific conversion op, the source register, ;; whether the input is signed, and finally the input and output ;; types. +(attr fpu_to_int_cvt (veri chain)) (decl fpu_to_int_cvt (FpuToIntOp Reg bool Type Type) Reg) (rule (fpu_to_int_cvt op src signed in_ty out_ty) (let ((size ScalarSize (scalar_size in_ty)) @@ -4409,6 +4629,7 @@ ;; does not fit in the target type. ;; Accepts the specific conversion op, the source register, ;; whether the input is signed, and finally the output type. +(attr fpu_to_int_cvt_sat (veri chain)) (decl fpu_to_int_cvt_sat (FpuToIntOp Reg bool Type) Reg) (rule 1 (fpu_to_int_cvt_sat op src _ $I64) (fpu_to_int op src)) @@ -4432,14 +4653,17 @@ (csel (Cond.Lt) min result)))) result)) +(attr signed_min (veri chain)) (decl signed_min (Type) Reg) (rule (signed_min $I8) (imm $I8 (ImmExtend.Sign) 0x80)) (rule (signed_min $I16) (imm $I16 (ImmExtend.Sign) 0x8000)) +(attr signed_max (veri chain)) (decl signed_max (Type) Reg) (rule (signed_max $I8) (imm $I8 (ImmExtend.Sign) 0x7F)) (rule (signed_max $I16) (imm $I16 (ImmExtend.Sign) 0x7FFF)) +(attr fpu_to_int (veri chain)) (decl fpu_to_int (FpuToIntOp Reg) Reg) (rule (fpu_to_int op src) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -4447,7 +4671,7 @@ dst)) ;; Helper for generating `MInst.IntToFpu` instructions. - +(attr int_to_fpu (veri chain)) (decl int_to_fpu (IntToFpuOp Reg) Reg) (rule (int_to_fpu op src) (let ((dst WritableReg (temp_writable_reg $I8X16)) @@ -4469,21 +4693,25 @@ (extern constructor gen_return_call_ind_info gen_return_call_ind_info) ;; Helper for creating `MInst.Call` instructions. +(attr call_impl (veri chain)) (decl call_impl (BoxCallInfo) SideEffectNoResult) (rule (call_impl info) (SideEffectNoResult.Inst (MInst.Call info))) ;; Helper for creating `MInst.CallInd` instructions. +(attr call_ind_impl (veri chain)) (decl call_ind_impl (BoxCallIndInfo) SideEffectNoResult) (rule (call_ind_impl info) (SideEffectNoResult.Inst (MInst.CallInd info))) ;; Helper for creating `MInst.ReturnCall` instructions. +(attr return_call_impl (veri chain)) (decl return_call_impl (BoxReturnCallInfo) SideEffectNoResult) (rule (return_call_impl info) (SideEffectNoResult.Inst (MInst.ReturnCall info))) ;; Helper for creating `MInst.ReturnCallInd` instructions. +(attr return_call_ind_impl (veri chain)) (decl return_call_ind_impl (BoxReturnCallIndInfo) SideEffectNoResult) (rule (return_call_ind_impl info) (SideEffectNoResult.Inst (MInst.ReturnCallInd info))) @@ -4511,6 +4739,7 @@ (dst Reg (vec_rrr (VecALUOp.And) dst tmp size))) dst)) +(attr vec_cmp (tag vector)) (decl vec_cmp (Reg Reg Type Cond) Reg) ;; Floating point Vs / Vc @@ -4602,6 +4831,7 @@ ;;;; TLS Values ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helper for emitting ElfTlsGetAddr. +(attr elf_tls_get_addr (veri chain)) (decl elf_tls_get_addr (ExternalName) Reg) (rule (elf_tls_get_addr name) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -4609,6 +4839,7 @@ (_ Unit (emit (MInst.ElfTlsGetAddr (box_external_name name) dst tmp)))) dst)) +(attr macho_tls_get_addr (veri chain)) (decl macho_tls_get_addr (ExternalName) Reg) (rule (macho_tls_get_addr name) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -4636,6 +4867,7 @@ (And (flags ProducesFlags) (cc1 Cond) (cc2 Cond)) )) +(attr cond_result_invert (veri chain)) (decl cond_result_invert (CondResult) CondResult) (rule (cond_result_invert (CondResult.Zero reg size)) (CondResult.NotZero reg size)) (rule (cond_result_invert (CondResult.NotZero reg size)) (CondResult.Zero reg size)) @@ -4645,6 +4877,7 @@ (rule (cond_result_invert (CondResult.And flags cc1 cc2)) (CondResult.Or flags (invert_cond cc1) (invert_cond cc2))) +(attr is_nonzero_cmp (veri chain)) (decl is_nonzero_cmp (Value) CondResult) (rule 1 (is_nonzero_cmp (maybe_uextend (fcmp _ cc a b))) (emit_fcmp cc a b)) @@ -4657,12 +4890,15 @@ ;; Note that this is used as the base entry case for instruction lowering such ;; as `select` and `brif`. The `Value` here is expected to, via CLIF validation, ;; have an integer type (and it can be I128) +(attr is_nonzero (veri chain)) (decl is_nonzero (Value) CondResult) (rule 0 (is_nonzero val @ (value_type (fits_in_32 ty))) (CondResult.NotZero (put_in_reg_zext32 val) (OperandSize.Size32))) (rule 1 (is_nonzero val @ (value_type $I64)) (CondResult.NotZero val (OperandSize.Size64))) -(rule 2 (is_nonzero val @ (value_type $I128)) + +(attr rule is_nonzero_i128 (tag i128)) +(rule is_nonzero_i128 2 (is_nonzero val @ (value_type $I128)) (let ((c ValueRegs (put_in_regs val)) (lo Reg (value_regs_get c 0)) (hi Reg (value_regs_get c 1))) @@ -4670,6 +4906,7 @@ (rule 3 (is_nonzero val @ (value_type $I8)) (CondResult.Cond (tst_imm $I32 val (u64_into_imm_logic $I32 255)) (Cond.Ne))) +(attr emit_icmp (veri chain)) (decl emit_icmp (IntCC Value Value) CondResult) ;; 8/16-bit base signed/unsigned cases @@ -4706,7 +4943,8 @@ (cond_code cc))) ;; 128-bit base case -(rule 4 (emit_icmp cc a @ (value_type $I128) b) +(attr rule emit_icmp_i128 (tag i128)) +(rule emit_icmp_i128 4 (emit_icmp cc a @ (value_type $I128) b) (let ((a_lo Reg (value_regs_get a 0)) (a_hi Reg (value_regs_get a 1)) (b_lo Reg (value_regs_get b 0)) @@ -4772,6 +5010,7 @@ (sbcs_side_effect $I64 a_hi b_hi)) (cond_code cc))) +(attr emit_fcmp (veri chain)) (decl emit_fcmp (FloatCC Value Value) CondResult) (rule (emit_fcmp cc a b @ (value_type ty)) (CondResult.Cond (fpu_cmp (scalar_size ty) a b) (fp_cond_code cc))) @@ -4792,6 +5031,7 @@ ;; 0110 on EQ, ;; 1000 on LT, ;; 0010 on GT. +(attr fp_cond_code (veri chain)) (decl fp_cond_code (FloatCC) Cond) ;; EQ | LT | GT. Vc => V clear. (rule (fp_cond_code (FloatCC.Ordered)) (Cond.Vc)) @@ -4823,81 +5063,35 @@ (type FlagsAndCC (enum (FlagsAndCC (flags ProducesFlags) (cc IntCC)))) -(spec (flags_and_cc flags cc) - (provide - (= result (concat (extract 67 64 flags) cc))) - (require - (or - (= cc (IntCC.Equal)) - (= cc (IntCC.NotEqual)) - (= cc (IntCC.UnsignedGreaterThanOrEqual)) - (= cc (IntCC.UnsignedGreaterThan)) - (= cc (IntCC.UnsignedLessThanOrEqual)) - (= cc (IntCC.UnsignedLessThan)) - (= cc (IntCC.SignedGreaterThanOrEqual)) - (= cc (IntCC.SignedGreaterThan)) - (= cc (IntCC.SignedLessThanOrEqual)) - (= cc (IntCC.SignedLessThan))))) ;; Helper constructor for `FlagsAndCC`. +(attr flags_and_cc (veri chain)) (decl flags_and_cc (ProducesFlags IntCC) FlagsAndCC) (rule (flags_and_cc flags cc) (FlagsAndCC.FlagsAndCC flags cc)) -(spec (flags_and_cc_to_bool a) - (provide - (= result - (switch (extract 7 0 a) - ((IntCC.Equal) (if (= (extract 10 10 a) #b1) #x01 #x00)) - ((IntCC.NotEqual) (if (= (extract 10 10 a) #b0) #x01 #x00)) - ((IntCC.SignedGreaterThan) (if (and (= (extract 10 10 a) #b0) (= (extract 11 11 a) (extract 8 8 a))) #x01 #x00)) - ((IntCC.SignedGreaterThanOrEqual) (if (= (extract 11 11 a) (extract 8 8 a)) #x01 #x00)) - ((IntCC.SignedLessThan) (if (not (= (extract 11 11 a) (extract 8 8 a))) #x01 #x00)) - ((IntCC.SignedLessThanOrEqual) (if (or (= (extract 10 10 a) #b1) (not (= (extract 11 11 a) (extract 8 8 a)))) #x01 #x00)) - ((IntCC.UnsignedGreaterThan) (if (and (= (extract 9 9 a) #b1) (= (extract 10 10 a) #b0)) #x01 #x00)) - ((IntCC.UnsignedGreaterThanOrEqual) (if (= (extract 9 9 a) #b1) #x01 #x00)) - ((IntCC.UnsignedLessThan) (if (= (extract 9 9 a) #b0) #x01 #x00)) - ((IntCC.UnsignedLessThanOrEqual) (if (or (= (extract 9 9 a) #b0) (= (extract 10 10 a) #b1)) #x01 #x00))))) - (require - (or - (= (extract 7 0 a) (IntCC.Equal)) - (= (extract 7 0 a) (IntCC.NotEqual)) - (= (extract 7 0 a) (IntCC.UnsignedGreaterThanOrEqual)) - (= (extract 7 0 a) (IntCC.UnsignedGreaterThan)) - (= (extract 7 0 a) (IntCC.UnsignedLessThanOrEqual)) - (= (extract 7 0 a) (IntCC.UnsignedLessThan)) - (= (extract 7 0 a) (IntCC.SignedGreaterThanOrEqual)) - (= (extract 7 0 a) (IntCC.SignedGreaterThan)) - (= (extract 7 0 a) (IntCC.SignedLessThanOrEqual)) - (= (extract 7 0 a) (IntCC.SignedLessThan))))) ;; Materialize a `FlagsAndCC` into a boolean `ValueRegs`. +(attr flags_and_cc_to_bool (veri chain)) (decl flags_and_cc_to_bool (FlagsAndCC) ValueRegs) (rule (flags_and_cc_to_bool (FlagsAndCC.FlagsAndCC flags cc)) (with_flags flags (cset (cond_code cc)))) ;; Get the `ProducesFlags` out of a `FlagsAndCC`. +(attr flags_and_cc_flags (veri chain)) (decl flags_and_cc_flags (FlagsAndCC) ProducesFlags) (rule (flags_and_cc_flags (FlagsAndCC.FlagsAndCC flags _cc)) flags) ;; Get the `IntCC` out of a `FlagsAndCC`. +(attr flags_and_cc_cc (veri chain)) (decl flags_and_cc_cc (FlagsAndCC) IntCC) (rule (flags_and_cc_cc (FlagsAndCC.FlagsAndCC _flags cc)) cc) ;; Determines the appropriate extend op given the value type and the given ArgumentExtension. -(spec (lower_extend_op ty b) - (provide - (= result - (switch ty - (8 (switch b ((ArgumentExtension.Sext) (ExtendOp.SXTB)) - ((ArgumentExtension.Uext) (ExtendOp.UXTB)))) - (16 (switch b ((ArgumentExtension.Sext) (ExtendOp.SXTH)) - ((ArgumentExtension.Uext) (ExtendOp.UXTH))))))) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) +(attr lower_extend_op (veri chain)) (decl lower_extend_op (Type ArgumentExtension) ExtendOp) (rule (lower_extend_op $I8 (ArgumentExtension.Sext)) (ExtendOp.SXTB)) (rule (lower_extend_op $I16 (ArgumentExtension.Sext)) (ExtendOp.SXTH)) (rule (lower_extend_op $I8 (ArgumentExtension.Uext)) (ExtendOp.UXTB)) (rule (lower_extend_op $I16 (ArgumentExtension.Uext)) (ExtendOp.UXTH)) - ; Recursion: bounded since recursive calls reduce type width (128-bit to 64-bit). (decl rec lower_bmask (Type Type ValueRegs) ValueRegs) @@ -4947,6 +5141,7 @@ (lower_bmask out_ty $I32 masked))) ;; Helpers for generating select instruction sequences. +(attr lower_select (veri chain)) (decl lower_select (Type CondResult Value Value) ValueRegs) (rule 1 (lower_select ty (CondResult.Zero reg size) a b) (lower_select_cond ty (cmp size reg (zero_reg)) (Cond.Eq) a b)) @@ -4967,6 +5162,7 @@ a b)) +(attr lower_select_cond (veri chain)) (decl lower_select_cond (Type ProducesFlags Cond Value Value) ValueRegs) (rule 2 (lower_select_cond (ty_scalar_float (fits_in_64 ty)) flags cond rn rm) (with_flags flags (fpu_csel ty cond rn rm))) @@ -4996,6 +5192,7 @@ (with_flags flags (csel cond rn rm))) ;; Helper for emitting `MInst.Jump` instructions. +(attr aarch64_jump (veri chain)) (decl aarch64_jump (BranchTarget) SideEffectNoResult) (rule (aarch64_jump target) (SideEffectNoResult.Inst (MInst.Jump target))) @@ -5018,6 +5215,7 @@ ;; PC-rel offset to the jumptable would be incorrect. ;; (The alternative is to introduce a relocation pass ;; for inlined jumptables, which is much worse, IMHO.) +(attr jt_sequence (veri chain)) (decl jt_sequence (Reg MachLabel BoxVecMachLabel) ConsumesFlags) (rule (jt_sequence ridx default targets) (let ((rtmp1 WritableReg (temp_writable_reg $I64)) @@ -5026,30 +5224,41 @@ (MInst.JTSequence default targets ridx rtmp1 rtmp2)))) ;; Helpers for emitting `MInst.CondBr` instructions. +;; Chainable helper terms: handled via chaining (inlined into specified +;; roots). With roots restricted to terms with explicit specs, these are +;; never verified standalone, so they need no spec and no todo tag. +(attr a64_br_cond (veri chain)) (decl a64_br_cond (Cond BranchTarget BranchTarget) ConsumesFlags) (rule (a64_br_cond cond taken not_taken) (ConsumesFlags.ConsumesFlagsSideEffect (MInst.CondBr taken not_taken (cond_br_cond cond)))) + +(attr a64_br_zero (veri chain)) (decl a64_br_zero (Reg OperandSize BranchTarget BranchTarget) SideEffectNoResult) (rule (a64_br_zero reg size taken not_taken) (SideEffectNoResult.Inst (MInst.CondBr taken not_taken (cond_br_zero reg size)))) + +(attr a64_br_not_zero (veri chain)) (decl a64_br_not_zero (Reg OperandSize BranchTarget BranchTarget) SideEffectNoResult) (rule (a64_br_not_zero reg size taken not_taken) (SideEffectNoResult.Inst (MInst.CondBr taken not_taken (cond_br_not_zero reg size)))) ;; Helper for emitting `MInst.TestBitAndBranch` instructions. +(attr test_branch (veri chain)) (decl test_branch (TestBitAndBranchKind BranchTarget BranchTarget Reg u8) SideEffectNoResult) (rule (test_branch kind taken not_taken rn bit) (SideEffectNoResult.Inst (MInst.TestBitAndBranch kind taken not_taken rn bit))) ;; Helper for emitting `tbnz` instructions. +(attr tbnz (veri chain)) (decl tbnz (BranchTarget BranchTarget Reg u8) SideEffectNoResult) (rule (tbnz taken not_taken rn bit) (test_branch (TestBitAndBranchKind.NZ) taken not_taken rn bit)) ;; Helper for emitting `tbz` instructions. +(attr tbz (veri chain)) (decl tbz (BranchTarget BranchTarget Reg u8) SideEffectNoResult) (rule (tbz taken not_taken rn bit) (test_branch (TestBitAndBranchKind.Z) taken not_taken rn bit)) @@ -5061,12 +5270,14 @@ (MInst.MovToNZCV rn))) ;; Helper for emitting `MInst.EmitIsland` instructions. +(attr emit_island (veri chain)) (decl emit_island (CodeOffset) SideEffectNoResult) (rule (emit_island needed_space) (SideEffectNoResult.Inst (MInst.EmitIsland needed_space))) ;; Helper for emitting `br_table` sequences. +(attr br_table_impl (veri chain)) (decl br_table_impl (u64 Reg MachLabel BoxVecMachLabel) Unit) (rule (br_table_impl (imm12_from_u64 jt_size) ridx default targets) (emit_side_effect (with_flags_side_effect @@ -5103,6 +5314,7 @@ (rule (vec_trn2 rn rm size) (vec_rrr (VecALUOp.Trn2) rn rm size)) ;; Helper for creating a zero value `ASIMDMovModImm` immediate. +(spec (asimd_mov_mod_imm_zero size) (provide (= (:imm result) #x00))) (decl asimd_mov_mod_imm_zero (ScalarSize) ASIMDMovModImm) (extern constructor asimd_mov_mod_imm_zero asimd_mov_mod_imm_zero) @@ -5111,6 +5323,47 @@ (extern constructor asimd_mov_mod_imm_from_u64 asimd_mov_mod_imm_from_u64) ;; Helper for fallibly creating an `ASIMDFPModImm` immediate from its parts. +(spec (asimd_fp_mod_imm_from_u64 value size) + (provide + (= (:size result) size) + (= (:imm result) + (switch size + ((ScalarSize.Size32) + (let ( + (v (extract 31 0 value)) + (b0_5 (bvand (bvlshr_int! v 19) #x0000003f)) + (b6 (bvand (bvlshr_int! v 19) #x00000040)) + (b7 (bvand (bvlshr_int! v 24) #x00000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + imm)) + ((ScalarSize.Size64) + (let ( + (b0_5 (bvand (bvlshr_int! value 48) #x000000000000003f)) + (b6 (bvand (bvlshr_int! value 48) #x0000000000000040)) + (b7 (bvand (bvlshr_int! value 56) #x0000000000000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + imm))))) + (match + (switch size + ((ScalarSize.Size32) + (let ( + (v (extract 31 0 value)) + (b0_5 (bvand (bvlshr_int! v 19) #x0000003f)) + (b6 (bvand (bvlshr_int! v 19) #x00000040)) + (b7 (bvand (bvlshr_int! v 24) #x00000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + (= v (asimd_fp_mod_imm_value32! imm)))) + ((ScalarSize.Size64) + (let ( + (b0_5 (bvand (bvlshr_int! value 48) #x000000000000003f)) + (b6 (bvand (bvlshr_int! value 48) #x0000000000000040)) + (b7 (bvand (bvlshr_int! value 56) #x0000000000000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + (= value (asimd_fp_mod_imm_value64! imm))))))) (decl pure partial asimd_fp_mod_imm_from_u64 (u64 ScalarSize) ASIMDFPModImm) (extern constructor asimd_fp_mod_imm_from_u64 asimd_fp_mod_imm_from_u64) @@ -5122,6 +5375,7 @@ dst)) ;; Helper for creating a `FpuLoad64` instruction +(attr fpu_load64 (veri chain)) (decl fpu_load64 (AMode MemFlagsData) Reg) (rule (fpu_load64 amode flags) (let ((dst WritableReg (temp_writable_reg $I8X16)) @@ -5136,6 +5390,7 @@ dst)) ;; Helper for creating an `LabelAddress` instruction. +(attr a64_label_address (veri chain)) (decl a64_label_address (MachLabel) Reg) (rule (a64_label_address label) (let ((dst WritableReg (temp_writable_reg $I64)) @@ -5146,3 +5401,294 @@ (decl a64_sequence_point () SideEffectNoResult) (rule (a64_sequence_point) (SideEffectNoResult.Inst (MInst.SequencePoint))) + +;;;; Verification terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(attr stack_addr (tag wasm_category_stack)) + +(attr fma (tag vector)) + +(attr isplit (tag vector)) + +(attr u64_replicated_u32 (tag vector)) + +(model Reg (type (bv 64))) + +(macro (n_set nzcv) (= (:N nzcv) #b1)) + +(macro (n_clear nzcv) (= (:N nzcv) #b0)) + +(macro (z_set nzcv) (= (:Z nzcv) #b1)) + +(macro (z_clear nzcv) (= (:Z nzcv) #b0)) + +(macro (c_set nzcv) (= (:C nzcv) #b1)) + +(macro (c_clear nzcv) (= (:C nzcv) #b0)) + +(macro (v_set nzcv) (= (:V nzcv) #b1)) + +(macro (v_clear nzcv) (= (:V nzcv) #b0)) + +(attr size_for_mov_to_fpu (veri chain)) + +(attr lower_cond_result_bool (veri chain)) + +(attr scalar_to_vector (tag vector)) + +(macro (cond_holds cc nzcv) + (match cc + ; EQ Equal. Z==1 + ((Eq) (z_set! nzcv)) + ; NE Not equal. Z==0 + ((Ne) (z_clear! nzcv)) + ; HS Unsigned higher or same (or carry set). C==1 + ((Hs) (c_set! nzcv)) + ; LO Unsigned lower (or carry clear). C==0 + ((Lo) (c_clear! nzcv)) + ; MI Negative. The mnemonic stands for "minus". N==1 + ((Mi) (n_set! nzcv)) + ; PL Positive or zero. The mnemonic stands for "plus". N==0 + ((Pl) (n_clear! nzcv)) + ; VS Signed overflow. The mnemonic stands for "V set". V==1 + ((Vs) (v_set! nzcv)) + ; VC No signed overflow. The mnemonic stands for "V clear". V==0 + ((Vc) (v_clear! nzcv)) + ; HI Unsigned higher. (C==1) && (Z==0) + ((Hi) (and (c_set! nzcv) (z_clear! nzcv))) + ; LS Unsigned lower or same. (C==0) || (Z==1) + ((Ls) (or (c_clear! nzcv) (z_set! nzcv))) + ; GE Signed greater than or equal. N==V + ((Ge) (= (:N nzcv) (:V nzcv))) + ; LT Signed less than. N!=V + ((Lt) (not (= (:N nzcv) (:V nzcv)))) + ; GT Signed greater than. (Z==0) && (N==V) + ((Gt) (and (z_clear! nzcv) (= (:N nzcv) (:V nzcv)))) + ; LE Signed less than or equal. (Z==1) || (N!=V) + ((Le) (or (z_set! nzcv) (not (= (:N nzcv) (:V nzcv))))) + ; AL (or omitted) Always executed. None tested. + ((Al) true) + ((Nv) false))) + +(macro (equals_move_wide_value n mov_wide_const) + (and + ; n equals the computed value + (= n + ; bits << (16 * shift) + (bvshl + (zero_ext 64 (:bits mov_wide_const)) + (bvmul (int2bv 64 16) (zero_ext 64 (:shift mov_wide_const))))) + + ; The zero case can be expressed in multiple ways. Assert that it + ; must be the zero shift value, matching the Rust implementation. + (=> (bv_is_zero! n) (= (:shift mov_wide_const) #b00)))) + +(macro (extend_op_src_width extend) + (match extend + ((UXTB) 8) + ((UXTH) 16) + ((UXTW) 32) + ((UXTX) 64) + ((SXTB) 8) + ((SXTH) 16) + ((SXTW) 32) + ((SXTX) 64))) + +(macro (extend_op_signed extend) + (match extend + ((UXTB) false) + ((UXTH) false) + ((UXTW) false) + ((UXTX) false) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true))) + +(macro (is_zero_ext dst src) + (and + (>= (widthof dst) (widthof src)) + (= dst (conv_to (widthof dst) (zero_ext 64 src))))) + +(macro (is_sign_ext dst src) + (and + (>= (widthof dst) (widthof src)) + (= dst (conv_to (widthof dst) (sign_ext 64 src))))) + +(macro (is_ext dst src) (or (is_zero_ext! dst src) (is_sign_ext! dst src))) + +(attr rule i128_alu_bitop (tag i128)) + +(attr rule lower_trapz (tag wasm_category_stack)) +(attr rule lower_trapnz (tag wasm_category_stack)) +(attr rule lower_brif (tag wasm_category_stack)) + +(attr fpu_move_128 (tag i128)) + +(macro (asimd_fp_mod_imm_value32 imm) + (let ( + ; let imm = imm as u32; + (v (zero_ext 32 imm)) + ; let b0_5 = imm & 0b111111; + (b0_5 (bvand v #x0000003f)) + ; let b6 = (imm >> 6) & 1; + (b6 (bvand (bvlshr_int! v 6) #x00000001)) + ; let b6_inv = b6 ^ 1; + (b6_inv (bvxor b6 #x00000001)) + ; let b7 = (imm >> 7) & 1; + (b7 (bvand (bvlshr_int! v 7) #x00000001)) + ) + ; b0_5 << 19 | (b6 * 0b11111) << 25 | b6_inv << 30 | b7 << 31 + (bvor (bvshl_int! b0_5 19) + (bvor (bvshl_int! (bvmul b6 #x0000001f) 25) + (bvor (bvshl_int! b6_inv 30) + (bvshl_int! b7 31)))))) + +(macro (asimd_fp_mod_imm_value64 imm) + (let ( + ; let imm = imm as u64; + (v (zero_ext 64 imm)) + ; let b0_5 = imm & 0b111111; + (b0_5 (bvand v #x000000000000003f)) + ; let b6 = (imm >> 6) & 1; + (b6 (bvand (bvlshr_int! v 6) #x0000000000000001)) + ; let b6_inv = b6 ^ 1; + (b6_inv (bvxor b6 #x0000000000000001)) + ; let b7 = (imm >> 7) & 1; + (b7 (bvand (bvlshr_int! v 7) #x0000000000000001)) + ) + ; b0_5 << 48 | (b6 * 0b11111111) << 54 | b6_inv << 62 | b7 << 63 + (bvor (bvshl_int! b0_5 48) + (bvor (bvshl_int! (bvmul b6 #x00000000000000ff) 54) + (bvor (bvshl_int! b6_inv 62) + (bvshl_int! b7 63)))))) + +(spec (shift_masked_imm ty imm) + (provide + (= result + (bvand + (extract 7 0 imm) + (extract 7 0 (shift_mask! 64 (:bits ty))))))) + +(spec (constant_f32 n) + (provide (= result (zero_ext 64 n)))) + +;; Other terms chained for verification +(attr br_cond_result (veri chain)) +(attr consumes_flags_get_reg (veri chain)) +(attr consumes_flags_get_regs (veri chain)) +(attr lo_reg (veri chain)) +(attr lower_return (veri chain)) +(attr multi_reg_to_pair (veri chain)) +(attr multi_reg_to_single (veri chain)) +(attr output_reg (veri chain)) +(attr output_value (veri chain)) +(attr produces_flags_concat (veri chain)) +(attr produces_flags_get_reg (veri chain)) +(attr produces_flags_ignore (veri chain)) +(attr put_in_reg_ext32 (veri chain)) +(attr side_effect (veri chain)) +(attr side_effect_concat (veri chain)) +(attr small_rotr (veri chain)) +(attr small_rotr_imm (veri chain)) +(attr temp_reg (veri chain)) +(attr trap_if_cond_result (veri chain)) +(attr with_flags_chained (veri chain)) +(attr with_flags_reg (veri chain)) +(attr with_flags_side_effect (veri chain)) + +;; Other terms not yet handled in verification + +(attr MInst.FpuMove32 (tag float)) +(attr MInst.FpuMove64 (tag float)) +(attr MInst.FpuMove128 (tag float)) +(attr MInst.FpuMoveFromVec (tag float)) +(attr MInst.FpuExtend (tag float)) +(attr MInst.FpuRR (tag float)) +(attr MInst.FpuRRR (tag float)) +(attr MInst.FpuRRI (tag float)) +(attr MInst.FpuRRIMod (tag float)) +(attr MInst.FpuRRRR (tag float)) +(attr MInst.FpuCmp (tag float)) +(attr MInst.FpuLoad16 (tag float)) +(attr MInst.FpuLoad16 (tag narrowfloat)) +(attr MInst.FpuStore16 (tag float)) +(attr MInst.FpuStore16 (tag narrowfloat)) +(attr MInst.FpuLoad32 (tag float)) +(attr MInst.FpuStore32 (tag float)) +(attr MInst.FpuLoad64 (tag float)) +(attr MInst.FpuStore64 (tag float)) +(attr MInst.FpuLoad128 (tag float)) +(attr MInst.FpuLoad128 (tag i128)) +(attr MInst.FpuStore128 (tag float)) +(attr MInst.FpuStore128 (tag i128)) +(attr MInst.FpuLoadP64 (tag float)) +(attr MInst.FpuStoreP64 (tag float)) +(attr MInst.FpuLoadP128 (tag float)) +(attr MInst.FpuLoadP128 (tag i128)) +(attr MInst.FpuStoreP128 (tag float)) +(attr MInst.FpuStoreP128 (tag i128)) +(attr MInst.FpuToInt (tag float)) +(attr MInst.IntToFpu (tag float)) +(attr MInst.FpuCSel16 (tag float)) +(attr MInst.FpuCSel32 (tag float)) +(attr MInst.FpuCSel64 (tag float)) +(attr MInst.FpuRound (tag float)) +(attr MInst.MovToFpu (tag float)) +(attr MInst.FpuMoveFPImm (tag float)) +(attr MInst.VecDup (tag vector)) +(attr MInst.VecDupFromFpu (tag vector)) +(attr MInst.VecDupFPImm (tag vector)) +(attr MInst.VecDupImm (tag vector)) +(attr MInst.VecExtend (tag vector)) +(attr MInst.VecMovElement (tag vector)) +(attr MInst.VecRRLong (tag vector)) +(attr MInst.VecRRNarrowLow (tag vector)) +(attr MInst.VecRRNarrowHigh (tag vector)) +(attr MInst.VecRRPair (tag vector)) +(attr MInst.VecRRRLong (tag vector)) +(attr MInst.VecRRRLongMod (tag vector)) +(attr MInst.VecRRPairLong (tag vector)) +(attr MInst.VecRRR (tag vector)) +(attr MInst.VecRRRMod (tag vector)) +(attr MInst.VecFmlaElem (tag vector)) +(attr MInst.VecMisc (tag vector)) +(attr MInst.VecLanes (tag vector)) +(attr MInst.VecShiftImm (tag vector)) +(attr MInst.VecShiftImmMod (tag vector)) +(attr MInst.VecExtract (tag vector)) +(attr MInst.VecTbl (tag vector)) +(attr MInst.VecTblExt (tag vector)) +(attr MInst.VecTbl2 (tag vector)) +(attr MInst.VecTbl2Ext (tag vector)) +(attr MInst.VecLoadReplicate (tag vector)) +(attr MInst.VecCSel (tag vector)) +(attr MInst.LoadAcquire (tag atomics)) +(attr extract_vector (tag vector)) + + +(attr fence (tag TODO)) +(attr bitrev (tag TODO)) +(attr call (tag TODO)) +(attr call_indirect (tag TODO)) +(attr consumes_flags_concat (tag TODO)) +(attr debugtrap (tag TODO)) +(attr dynamic_stack_addr (tag TODO)) +(attr f128const (tag TODO)) +(attr f16const (tag TODO)) +(attr fcvt_to_uint_sat (tag TODO)) +(attr get_return_address (tag TODO)) +(attr lower_bmask (tag TODO)) +(attr nop (tag TODO)) +(attr return (tag TODO)) +(attr return_call (tag TODO)) +(attr return_call_indirect (tag TODO)) +(attr sadd_overflow (tag TODO)) +(attr sequence_point (tag TODO)) +(attr ssub_overflow (tag TODO)) +(attr tls_model (tag TODO)) +(attr u16_from_u32 (tag TODO)) +(attr uadd_overflow (tag TODO)) +(attr usub_overflow (tag TODO)) +(attr vhigh_bits (tag TODO)) +(attr with_flags (tag TODO)) +(attr get_exception_handler_address (tag TODO)) diff --git a/cranelift/codegen/src/isa/aarch64/inst/imms.rs b/cranelift/codegen/src/isa/aarch64/inst/imms.rs index 066c7621675a..10878c7ed623 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/imms.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/imms.rs @@ -1,5 +1,7 @@ //! AArch64 ISA definitions: immediate constants. +#![expect(missing_docs, reason = "fields mostly self-describing")] + use crate::ir::types::*; use crate::isa::aarch64::inst::{OperandSize, ScalarSize}; use crate::machinst::PrettyPrint; @@ -624,10 +626,10 @@ impl MoveWideConst { /// Advanced SIMD modified immediate as used by MOVI/MVNI. #[derive(Clone, Copy, Debug, PartialEq)] pub struct ASIMDMovModImm { - imm: u8, - shift: u8, - is_64bit: bool, - shift_ones: bool, + pub imm: u8, + pub shift: u8, + pub is_64bit: bool, + pub shift_ones: bool, } impl ASIMDMovModImm { @@ -747,8 +749,8 @@ impl ASIMDMovModImm { /// Advanced SIMD modified immediate as used by the vector variant of FMOV. #[derive(Clone, Copy, Debug, PartialEq)] pub struct ASIMDFPModImm { - imm: u8, - size: ScalarSize, + pub imm: u8, + pub size: ScalarSize, } impl ASIMDFPModImm { diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs index c550b6dc053c..298185f89fda 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs @@ -16,7 +16,7 @@ use core::slice; use smallvec::{SmallVec, smallvec}; pub(crate) mod regs; -pub(crate) use self::regs::*; +pub use self::regs::*; pub mod imms; pub use self::imms::*; pub mod args; @@ -1233,7 +1233,11 @@ fn pretty_print_try_call(info: &TryCallInfo) -> String { } impl Inst { - fn print_with_state(&self, state: &mut EmitState) -> String { + #[expect( + missing_docs, + reason = "exposed for cranelift-isle/veri pretty-printing" + )] + pub fn print_with_state(&self, state: &mut EmitState) -> String { fn op_name(alu_op: ALUOp) -> &'static str { match alu_op { ALUOp::Add => "add", diff --git a/cranelift/codegen/src/isa/aarch64/inst/regs.rs b/cranelift/codegen/src/isa/aarch64/inst/regs.rs index 7dc0c91f3319..67921403f28c 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/regs.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/regs.rs @@ -1,4 +1,5 @@ //! AArch64 ISA definitions: registers. +#![expect(missing_docs, reason = "fields mostly self-describing")] use crate::isa::aarch64::inst::OperandSize; use crate::isa::aarch64::inst::ScalarSize; @@ -47,7 +48,6 @@ pub(crate) const fn vreg_preg(num: u8) -> PReg { } /// Get a writable reference to a V-register. -#[cfg(test)] // Used only in test code. pub fn writable_vreg(num: u8) -> Writable { Writable::from_reg(vreg(num)) } diff --git a/cranelift/codegen/src/isa/aarch64/inst_neon.isle b/cranelift/codegen/src/isa/aarch64/inst_neon.isle index 2b6fd5792e91..5f499495a3c0 100644 --- a/cranelift/codegen/src/isa/aarch64/inst_neon.isle +++ b/cranelift/codegen/src/isa/aarch64/inst_neon.isle @@ -1,8 +1,7 @@ - ;; Move helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(attr fpu_move_128 (tag i128)) (decl fpu_move_128 (Reg) Reg) (rule (fpu_move_128 src) (let ((dst WritableReg (temp_writable_reg $I8X16)) (_ Unit (emit (MInst.FpuMove128 dst src)))) (writable_reg_to_reg dst))) - diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle index 28eea60536cd..b20545f93e90 100644 --- a/cranelift/codegen/src/isa/aarch64/lower.isle +++ b/cranelift/codegen/src/isa/aarch64/lower.isle @@ -3,7 +3,38 @@ ;; The main lowering constructor term: takes a clif `Inst` and returns the ;; register(s) within which the lowered instruction's result values live. (spec (lower arg) - (provide (= result arg))) + (provide + ; On successful execution, computation results match. + (if (not clif_trap) + ; Results agree. + (= result arg) + ; If we expect a CLIF trap, it should happen in execution + ; also. + exec_trap) + + ; Should trap on both sides, or neither. + (= clif_trap exec_trap) + + ; Load effects + + ; Either both active, or both not. + (= (:active clif_load) (:active isa_load)) + + ; If active, their parameters must match. + (=> (:active clif_load) (= clif_load isa_load)) + + ; Store effects + + ; Either both active, or both not. + (= (:active clif_store) (:active isa_store)) + ; If active, their parameters must agree. + (=> (:active clif_store) + (and + (= (:size_bits clif_store) (:size_bits isa_store)) + (= (:addr clif_store) (:addr isa_store)) + (= + (conv_to (:size_bits clif_store) (:value clif_store)) + (conv_to (:size_bits clif_store) (:value isa_store))))))) (decl partial lower (Inst) InstOutput) ;; Variant of the main lowering constructor term, which receives an @@ -51,55 +82,53 @@ ;; `i64` and smaller ;; Base case, simply adding things in registers. -(rule iadd_base_case -1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x y))) +(rule iadd_base_case -1 (lower (iadd (ty_int_ref_scalar_64_extract ty) x y)) (add ty x y)) ;; Special cases for when one operand is an immediate that fits in 12 bits. -(rule iadd_imm12_right 4 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (imm12_from_value y)))) +(rule iadd_imm12_right 4 (lower (iadd (ty_int_ref_scalar_64_extract ty) x (imm12_from_value y))) (add_imm ty x y)) -(rule iadd_imm12_left 5 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (imm12_from_value x) y))) +(rule iadd_imm12_left 5 (lower (iadd (ty_int_ref_scalar_64_extract ty) (imm12_from_value x) y)) (add_imm ty y x)) ;; Same as the previous special cases, except we can switch the addition to a ;; subtraction if the negated immediate fits in 12 bits. -(rule iadd_imm12_neg_right 2 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x y))) +(rule iadd_imm12_neg_right 2 (lower (iadd (ty_int_ref_scalar_64_extract ty) x y)) (if-let imm12_neg (imm12_from_negated_value y)) (sub_imm ty x imm12_neg)) -(rule iadd_imm12_neg_left 3 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x y))) +(rule iadd_imm12_neg_left 3 (lower (iadd (ty_int_ref_scalar_64_extract ty) x y)) (if-let imm12_neg (imm12_from_negated_value x)) (sub_imm ty y imm12_neg)) ;; Special cases for when we're adding an extended register where the extending ;; operation can get folded into the add itself. -(rule iadd_extend_right 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (extended_value_from_value y)))) +(rule iadd_extend_right 0 (lower (iadd (ty_int_ref_scalar_64_extract ty) x (extended_value_from_value y))) (add_extend ty x y)) -(rule iadd_extend_left 1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (extended_value_from_value x) y))) +(rule iadd_extend_left 1 (lower (iadd (ty_int_ref_scalar_64_extract ty) (extended_value_from_value x) y)) (add_extend ty y x)) ;; Special cases for when we're adding the shift of a different ;; register by a constant amount and the shift can get folded into the add. -(rule iadd_ishl_right 7 (lower (has_type (ty_int_ref_scalar_64 ty) - (iadd _ x (ishl _ y (iconst _ k))))) +(rule iadd_ishl_right 7 (lower (iadd (ty_int_ref_scalar_64_extract ty) x (ishl _ y (iconst _ k)))) (if-let amt (lshl_from_imm64 ty k)) (add_shift ty x y amt)) -(rule iadd_ishl_left 6 (lower (has_type (ty_int_ref_scalar_64 ty) - (iadd _ (ishl _ x (iconst _ k)) y))) +(rule iadd_ishl_left 6 (lower (iadd (ty_int_ref_scalar_64_extract ty) (ishl _ x (iconst _ k)) y)) (if-let amt (lshl_from_imm64 ty k)) (add_shift ty y x amt)) ;; Fold an `iadd` and `imul` combination into a `madd` instruction. -(rule iadd_imul_right 7 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (imul _ y z)))) +(rule iadd_imul_right 7 (lower (iadd (ty_int_ref_scalar_64_extract ty) x (imul _ y z))) (madd ty y z x)) -(rule iadd_imul_left 6 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (imul _ x y) z))) +(rule iadd_imul_left 6 (lower (iadd (ty_int_ref_scalar_64_extract ty) (imul _ x y) z)) (madd ty x y z)) ;; Fold an `isub` and `imul` combination into a `msub` instruction. -(rule isub_imul (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x (imul _ y z)))) +(rule isub_imul (lower (isub (ty_int_ref_scalar_64_extract ty) x (imul _ y z))) (msub ty y z x)) ;; vectors @@ -108,7 +137,8 @@ (add_vec x y (vector_size ty))) ;; `i128` -(rule -3 (lower (has_type $I128 (iadd _ x y))) +(attr rule iadd_i128 (tag i128)) +(rule iadd_i128 -3 (lower (has_type $I128 (iadd _ x y))) (let ;; Get the high/low registers for `x`. ((x_regs ValueRegs x) @@ -273,8 +303,8 @@ (smulh $I64 (put_in_reg_sext64 x) (put_in_reg_sext64 y)))) ;;;; Rules for `iconcat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(rule (lower (has_type $I128 (iconcat _ lo hi))) +(attr rule iconcat_i128 (tag i128)) +(rule iconcat_i128 (lower (has_type $I128 (iconcat _ lo hi))) (output (value_regs lo hi))) ;;;; Rules for `scalar_to_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -534,8 +564,8 @@ (fpu_round (FpuRoundMode.Nearest64) x)) ;;;; Rules for `fma` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(rule 1 (lower (has_type ty (fma _ x y z))) (fmadd ty x y z)) +(attr rule fma (tag vector)) +(rule fma 1 (lower (fma ty x y z)) (fmadd ty x y z)) (rule 2 (lower (has_type (ty_scalar_float ty) (fma _ x y (fneg _ z)))) (fnmsub ty x y z)) ;; Constructors matching the scalar behavior of aarch64. If you're confused like @@ -545,9 +575,16 @@ ;; * fnmadd r = -(a * b) - c / multiply, then negate, then subtract ;; * fmsub r = -(a * b) + c / multiply, then negate, then add ;; * fnmsub r = a * b - c / multiply, then subtract +(attr fmadd (tag vector)) (decl fmadd (Type Value Value Value) Reg) + +(attr fnmadd (tag vector)) (decl fnmadd (Type Value Value Value) Reg) + +(attr fmsub (tag vector)) (decl fmsub (Type Value Value Value) Reg) + +(attr fnmsub (tag vector)) (decl fnmsub (Type Value Value Value) Reg) ;; Switch `fm{add,sub}` to `fnm{add,sub}` if one of the operands are negated @@ -569,6 +606,7 @@ ;; Lowers a fused-multiply-add operation handling various forms of the ;; instruction to get maximal coverage of what's available on AArch64. +(attr lower_fmla (tag vector)) (decl lower_fmla (VecALUModOp Value Value Value VectorSize) Reg) ;; Base case, emit the op requested. @@ -629,7 +667,7 @@ (rule 1 (lower (has_type $I64 (fcvt_to_sint _ x @ (value_type $F32)))) (fpu_to_int_cvt (FpuToIntOp.F32ToI64) x true $F32 $I64)) -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint _ x @ (value_type $F64)))) +(rule fcvt_to_sint (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint _ x @ (value_type $F64)))) (fpu_to_int_cvt (FpuToIntOp.F64ToI32) x true $F64 out_ty)) (rule 1 (lower (has_type $I64 (fcvt_to_sint _ x @ (value_type $F64)))) @@ -720,28 +758,27 @@ ;; `i64` and smaller ;; Base case, simply subtracting things in registers. -(rule isub_base_case -4 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x y))) +(rule isub_base_case -4 (lower (isub (ty_int_ref_scalar_64_extract ty) x y)) (sub ty x y)) ;; Special case for when one operand is an immediate that fits in 12 bits. -(rule isub_imm12 0 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x (imm12_from_value y)))) +(rule isub_imm12 0 (lower (isub (ty_int_ref_scalar_64_extract ty) x (imm12_from_value y))) (sub_imm ty x y)) ;; Same as the previous special case, except we can switch the subtraction to an ;; addition if the negated immediate fits in 12 bits. -(rule isub_imm12_neg 2 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x y))) +(rule isub_imm12_neg 2 (lower (isub (ty_int_ref_scalar_64_extract ty) x y)) (if-let imm12_neg (imm12_from_negated_value y)) (add_imm ty x imm12_neg)) ;; Special cases for when we're subtracting an extended register where the ;; extending operation can get folded into the sub itself. -(rule isub_extend 1 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x (extended_value_from_value y)))) +(rule isub_extend 1 (lower (isub (ty_int_ref_scalar_64_extract ty) x (extended_value_from_value y))) (sub_extend ty x y)) ;; Finally a special case for when we're subtracting the shift of a different ;; register by a constant amount and the shift can get folded into the sub. -(rule isub_ishl -3 (lower (has_type (ty_int_ref_scalar_64 ty) - (isub _ x (ishl _ y (iconst _ k))))) +(rule isub_ishl -3 (lower (isub (ty_int_ref_scalar_64_extract ty) x (ishl _ y (iconst _ k)))) (if-let amt (lshl_from_imm64 ty k)) (sub_shift ty x y amt)) @@ -750,7 +787,8 @@ (sub_vec x y (vector_size ty))) ;; `i128` -(rule -1 (lower (has_type $I128 (isub _ x y))) +(attr rule isub_i128 (tag i128)) +(rule isub_i128 -1 (lower (isub $I128 x y)) (sub_i128 x y)) ;;;; Rules for `uadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -790,11 +828,12 @@ ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller. -(rule imul_base_case -3 (lower (has_type (ty_int_ref_scalar_64 ty) (imul _ x y))) +(rule imul_base_case -3 (lower (imul (ty_int_ref_scalar_64_extract ty) x y)) (madd ty x y (zero_reg))) ;; `i128`. -(rule -1 (lower (has_type $I128 (imul _ x y))) +(attr rule imul_i128 (tag i128)) +(rule imul_i128 -1 (lower (imul $I128 x y)) (let ;; Get the high/low registers for `x`. ((x_regs ValueRegs x) @@ -874,7 +913,7 @@ ;; xtn tmp2.2s, rm.2d ;; shll rd.2d, rd.2s, #32 ;; umlal rd.2d, tmp2.2s, tmp1.2s -(rule -1 (lower (has_type $I64X2 (imul _ x y))) +(rule -1 (lower (imul $I64X2 x y)) (let ((rn Reg x) (rm Reg y) ;; Reverse the 32-bit elements in the 64-bit words. @@ -1020,10 +1059,7 @@ ;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero. ;; It takes a value and extension type, and performs the appropriate checks. -;; TODO: restore spec -; (spec (put_nonzero_in_reg_sext64 x) -; (provide (= (sign_ext 64 x) result)) -; (require (not (= #x0000000000000000 result)))) +(attr put_nonzero_in_reg (veri chain)) (decl put_nonzero_in_reg (Value ExtType Type) Reg) ;; Special case where if a `Value` is known to be nonzero we can trivially @@ -1049,10 +1085,10 @@ ;; Note that aarch64's `udiv` doesn't trap so to respect the semantics of ;; CLIF's `udiv` the check for zero needs to be manually performed. -(rule udiv 1 (lower (has_type $I64 (udiv _ x y))) +(rule udiv_54 1 (lower (udiv $I64 x y)) (a64_udiv $I64 (put_in_reg x) (put_nonzero_in_reg y (ExtType.Unsigned) $I64))) -(rule udiv (lower (has_type (fits_in_32 ty) (udiv _ x y))) +(rule udiv_fits_in_32 (lower (udiv (fits_in_32 ty) x y)) (a64_udiv $I32 (put_in_reg_zext32 x) (put_nonzero_in_reg y (ExtType.Unsigned) ty))) ;;;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1078,7 +1114,7 @@ ;; TODO: if `y` is -1 then a check that `x` is not INT_MIN is all that's ;; necessary, but right now `y` is checked to not be -1 as well. -(rule sdiv_base_case (lower (has_type $I64 (sdiv _ x y))) +(rule sdiv_base_case_64 (lower (sdiv $I64 x y)) (let ((x64 Reg (put_in_reg_sext64 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) $I64)) (intmin_check_x Reg (intmin_check $I64 x64)) @@ -1086,7 +1122,7 @@ (result Reg (a64_sdiv $I64 valid_x64 y64))) result)) -(rule sdiv_base_case -1 (lower (has_type (fits_in_32 ty) (sdiv _ x y))) +(rule sdiv_base_case_fits_in_32 -1 (lower (sdiv (fits_in_32 ty) x y)) (let ((x32 Reg (put_in_reg_sext32 x)) (y32 Reg (put_nonzero_in_reg y (ExtType.Signed) ty)) (intmin_check_x Reg (intmin_check ty x32)) @@ -1096,14 +1132,15 @@ ;; Special case for `sdiv` where no checks are needed due to division by a ;; constant meaning the checks are always passed. -(rule sdiv_safe_divisor 2 (lower (has_type $I64 (sdiv _ x (iconst _ imm)))) +(rule sdiv_safe_divisor_i64 2 (lower (sdiv $I64 x (iconst _ imm))) (if-let y (safe_divisor_from_imm64 $I64 imm)) (a64_sdiv $I64 (put_in_reg_sext64 x) (imm $I64 (ImmExtend.Sign) y))) -(rule sdiv_safe_divisor 1 (lower (has_type (fits_in_32 ty) (sdiv _ x (iconst _ imm)))) +(rule sdiv_safe_divisor_fits_in_32 1 (lower (sdiv (fits_in_32 ty) x (iconst _ imm))) (if-let y (safe_divisor_from_imm64 ty imm)) (a64_sdiv ty (put_in_reg_sext32 x) (imm ty (ImmExtend.Sign) y))) + ;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero. ;;;; Rules for `urem` and `srem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1122,28 +1159,28 @@ ;; TODO: we can avoid a 0 check, if the dividend is a non-0 constant -(rule urem (lower (has_type $I64 (urem _ x y))) +(rule urem_64 (lower (urem $I64 x y)) (let ((x64 Reg (put_in_reg_zext64 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Unsigned) $I64)) (div Reg (a64_udiv $I64 x64 y64)) (result Reg (msub $I64 div y64 x64))) result)) -(rule urem -1 (lower (has_type (fits_in_32 ty) (urem _ x y))) +(rule urem_fits_in_32 -1 (lower (urem (fits_in_32 ty) x y)) (let ((x64 Reg (put_in_reg_zext32 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Unsigned) ty)) (div Reg (a64_udiv ty x64 y64)) (result Reg (msub ty div y64 x64))) result)) -(rule srem (lower (has_type $I64 (srem _ x y))) +(rule srem_64 (lower (srem $I64 x y)) (let ((x64 Reg (put_in_reg_sext64 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) $I64)) (div Reg (a64_sdiv $I64 x64 y64)) (result Reg (msub $I64 div y64 x64))) result)) -(rule srem -1 (lower (has_type (fits_in_32 ty) (srem _ x y))) +(rule srem_fits_in_32 -1 (lower (srem (fits_in_32 ty) x y)) (let ((x64 Reg (put_in_reg_sext32 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) ty)) (div Reg (a64_sdiv ty x64 y64)) @@ -1156,25 +1193,7 @@ ;; cmp $x, $y ;; csel .., $x, $y, $cc - (spec (cmp_and_choose ty cc signed x y) - (provide - (= result - (switch cc - (#x03 (if (bvule x y) x y)) - (#x08 (if (bvuge x y) x y)) - (#x0b (if (bvsle x y) x y)) - (#x0c (if (bvsge x y) x y))))) - (require - (or (= ty 8) - (= ty 16) - (= ty 32) - (= ty 64)) - (or (= cc #x03) - (= cc #x08) - (= cc #x0b) - (= cc #x0c)) - (if signed (or (= cc #x0b) (= cc #x0c)) - (or (= cc #x03) (= cc #x08))))) +(attr cmp_and_choose (veri chain)) (decl cmp_and_choose (Type Cond bool Value Value) ValueRegs) (rule (cmp_and_choose (fits_in_64 ty) cc _ x y) (let ((x Reg (put_in_reg x)) @@ -1184,7 +1203,8 @@ ;; `i16` and `i8` min/max require sign extension as ;; the comparison operates on (at least) 32 bits. -(rule 1 (cmp_and_choose (fits_in_16 ty) cc signed x y) +(attr rule cmp_and_choose_8_16 (veri priority)) +(rule cmp_and_choose_8_16 1 (cmp_and_choose (fits_in_16 ty) cc signed x y) (let ((x Reg (extend (put_in_reg x) signed (ty_bits ty) 32)) (y Reg (extend (put_in_reg y) signed (ty_bits ty) 32))) (with_flags_reg (cmp (operand_size ty) x y) @@ -1248,7 +1268,8 @@ ;; Conversion to 128-bit needs a zero-extension of the lower bits and the upper ;; bits are all zero. -(rule -1 (lower (has_type $I128 (uextend _ x))) +(attr rule uextend_i128 (tag i128)) +(rule uextend_i128 -1 (lower (uextend $I128 x)) (value_regs (put_in_reg_zext64 x) (imm $I64 (ImmExtend.Zero) 0))) ;; Like above where vector extraction automatically zero-extends extending to @@ -1259,11 +1280,13 @@ (value_regs (mov_from_vec (put_in_reg vec) lane (lane_size in)) (imm $I64 (ImmExtend.Zero) 0))) ;; Zero extensions from a load can be encoded in the load itself -(rule (lower (has_type (fits_in_64 _) (uextend _ x @ (has_type in_ty (load _ (little_or_native_endian flags) address offset))))) +(attr rule uextend_load (tag slow)) +(rule uextend_load (lower (uextend (fits_in_64 _) x @ (has_type in_ty (load _ (little_or_native_endian flags) address offset)))) (if-let inst (is_sinkable_inst x)) (let ((_ Unit (sink_inst inst))) (aarch64_uload in_ty (amode in_ty address offset) flags))) +(attr aarch64_uload (veri chain)) (decl aarch64_uload (Type AMode MemFlagsData) Reg) (rule (aarch64_uload $I8 amode flags) (aarch64_uload8 amode flags)) (rule (aarch64_uload $I16 amode flags) (aarch64_uload16 amode flags)) @@ -1287,7 +1310,8 @@ (size_from_ty out))) ;; 64-bit to 128-bit only needs to sign-extend the input to the upper bits. -(rule -2 (lower (has_type $I128 (sextend _ x))) +(attr rule sextend_i128 (tag i128)) +(rule sextend_i128 -2 (lower (sextend $I128 x)) (let ((lo Reg (put_in_reg_sext64 x)) (hi Reg (asr_imm $I64 lo (imm_shift_from_u8 63)))) (value_regs lo hi))) @@ -1318,11 +1342,13 @@ (value_regs lo hi))) ;; Signed extensions from a load can be encoded in the load itself -(rule (lower (has_type (fits_in_64 _) (sextend _ x @ (has_type in_ty (load _ (little_or_native_endian flags) address offset))))) +(attr rule sextend_load (tag slow)) +(rule sextend_load (lower (sextend (fits_in_64 _) x @ (has_type in_ty (load _ (little_or_native_endian flags) address offset)))) (if-let inst (is_sinkable_inst x)) (let ((_ Unit (sink_inst inst))) (aarch64_sload in_ty (amode in_ty address offset) flags))) +(attr aarch64_sload (veri chain)) (decl aarch64_sload (Type AMode MemFlagsData) Reg) (rule (aarch64_sload $I8 amode flags) (aarch64_sload8 amode flags)) (rule (aarch64_sload $I16 amode flags) (aarch64_sload16 amode flags)) @@ -1460,14 +1486,14 @@ ;; they sum to the type width then it means that the shifts don't actually do ;; anything CLIF-wise and this should compile down to a `bor` operation. Leave ;; that edge case to the mid-end and only lower to `extr` here. -(rule 5 (lower (has_type (ty_32_or_64 ty) - (bor _ (ishl _ x (u8_from_iconst xs)) (ushr _ y (u8_from_iconst ys))))) +(rule extr_32_or_64 5 (lower + (bor (ty_32_or_64 ty) (ishl _ x (u8_from_iconst xs)) (ushr _ y (u8_from_iconst ys)))) (if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys))) (if-let true (u64_gt xs 0)) (if-let true (u64_gt ys 0)) (a64_extr ty x y (imm_shift_from_u8 ys))) -(rule 5 (lower (has_type (ty_32_or_64 ty) - (bor _ (ushr _ y (u8_from_iconst ys)) (ishl _ x (u8_from_iconst xs))))) +(rule extr_32_or_64_2 5 (lower + (bor (ty_32_or_64 ty) (ushr _ y (u8_from_iconst ys)) (ishl _ x (u8_from_iconst xs)))) (if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys))) (if-let true (u64_gt xs 0)) (if-let true (u64_gt ys 0)) @@ -1559,44 +1585,7 @@ ;; ;; Note that this automatically handles the clif semantics of masking the ;; shift amount where necessary. - (spec (do_shift op t a b) - (provide - (= result - (switch op - ((ALUOp.Lsr) (conv_to 64 - (bvlshr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Asr) (conv_to 64 - (bvashr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Lsl) (conv_to 64 - (bvshl (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b))))))))) - (require - (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl))) - (= t (widthof b)) - (or (= t 8) (= t 16) (= t 32) (= t 64)) - (switch op - ((ALUOp.Lsr) (switch t - (8 (= (extract 31 0 a) (zero_ext 32 (extract 7 0 a)))) - (16 (= (extract 31 0 a) (zero_ext 32 (extract 15 0 a)))) - (32 true) - (64 true))) - ((ALUOp.Asr) (switch t - (8 (= (extract 31 0 a) (sign_ext 32 (extract 7 0 a)))) - (16 (= (extract 31 0 a) (sign_ext 32 (extract 15 0 a)))) - (32 true) - (64 true))) - ((ALUOp.Lsl) true)))) -(instantiate do_shift - ((args (bv 8) Int (bv 64) (bv 8)) (ret (bv 64)) (canon (bv 8))) - ((args (bv 8) Int (bv 64) (bv 16)) (ret (bv 64)) (canon (bv 16))) - ((args (bv 8) Int (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 8) Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) +(attr do_shift (veri chain)) (decl do_shift (ALUOp Type Reg Value) Reg) ;; 8/16-bit shift base case. @@ -1613,8 +1602,7 @@ (masked_shift_amt Reg (and_imm $I32 shift_amt (shift_mask ty)))) (alu_rrr op $I32 x masked_shift_amt))) - (spec (shift_mask t) - (provide (= (bvsub (int2bv 64 t) #x0000000000000001) result))) +(spec (shift_mask ty) (provide (= result (shift_mask! 64 (:bits ty))))) (decl shift_mask (Type) ImmLogic) (extern constructor shift_mask shift_mask) @@ -1798,9 +1786,9 @@ ;; imm.imm &= size - 1; ;; imm ;; } - (spec (negate_imm_shift ty x) - (provide - (= result (bvand (bvsub (int2bv 6 ty) x) (bvsub (int2bv 6 ty) #b000001))))) +(spec (negate_imm_shift ty x) + (provide (= result + (bvand (bvsub (int2bv 6 (:bits ty)) x) (bvsub (int2bv 6 (:bits ty)) #b000001))))) (decl negate_imm_shift (Type ImmShift) ImmShift) (extern constructor negate_imm_shift negate_imm_shift) @@ -1858,19 +1846,6 @@ ;; lsr val_rshift, val, masked_amt ;; lsl val_lshift, val, neg_amt ;; orr rd, val_lshift val_rshift - (spec (small_rotr t x y) - (provide - (= result - (switch t - (8 (conv_to 64 (rotr (extract 7 0 x) (extract 7 0 y)))) - (16 (conv_to 64 (rotr (extract 15 0 x) (extract 15 0 y))))))) - (require - (or (= t 8) (= t 16)) - (switch t - (8 (= (extract 31 8 x) #x000000)) - (16 (= (extract 31 16 x) #x0000))))) -(instantiate small_rotr - ((args Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64)))) (decl small_rotr (Type Reg Reg) Reg) (rule small_rotr (small_rotr ty val amt) (let ((masked_amt Reg (and_imm $I32 amt (rotr_mask ty))) @@ -1880,7 +1855,7 @@ (val_lshift Reg (lsl $I32 val neg_amt))) (orr $I32 val_lshift val_rshift))) -(spec (rotr_mask x) (provide (= (bvsub (int2bv 64 x) #x0000000000000001) result))) +(spec (rotr_mask ty) (provide (= (bvsub (int2bv 64 (:bits ty)) #x0000000000000001) result))) (decl rotr_mask (Type) ImmLogic) (extern constructor rotr_mask rotr_mask) @@ -1894,20 +1869,6 @@ ;; lsl val_lshift, val, ;; orr rd, val_lshift, val_rshift -(spec (small_rotr_imm t x y) - (provide - (= result - (switch t - (8 (conv_to 64 (rotr (extract 7 0 x) (zero_ext 8 y)))) - (16 (conv_to 64 (rotr (extract 15 0 x) (zero_ext 16 y))))))) - (require - (or (= t 8) (= t 16)) - (switch t - (8 (= (extract 31 8 x) #x000000)) - (16 (= (extract 31 16 x) #x0000))) - (bvult y (int2bv 6 t)))) -(instantiate small_rotr_imm - ((args Int (bv 64) (bv 6)) (ret (bv 64)) (canon (bv 64)))) (decl small_rotr_imm (Type Reg ImmShift) Reg) (rule small_rotr_imm (small_rotr_imm ty val amt) (let ((val_rshift Reg (lsr_imm $I32 val amt)) @@ -1915,8 +1876,8 @@ (orr $I32 val_lshift val_rshift))) (spec (rotr_opposite_amount ty x) - (provide - (= (bvsub (int2bv 6 ty) (bvand x (bvsub (int2bv 6 ty) #b000001))) result))) + (provide + (= (bvsub (int2bv 6 (:bits ty)) (bvand x (bvsub (int2bv 6 (:bits ty)) #b000001))) result))) (decl rotr_opposite_amount (Type ImmShift) ImmShift) (extern constructor rotr_opposite_amount rotr_opposite_amount) @@ -1956,12 +1917,12 @@ (rule -1 (lower (has_type ty (bitrev _ x))) (rbit ty x)) - ;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - +(attr rule clz_8 (veri priority)) (rule clz_8 (lower (has_type $I8 (clz _ x))) (sub_imm $I32 (a64_clz $I32 (put_in_reg_zext32 x)) (u8_into_imm12 24))) +(attr rule clz_16 (veri priority)) (rule clz_16 (lower (has_type $I16 (clz _ x))) (sub_imm $I32 (a64_clz $I32 (put_in_reg_zext32 x)) (u8_into_imm12 16))) @@ -1988,10 +1949,11 @@ ;; Note that all `ctz` instructions are implemented by reversing the bits and ;; then using a `clz` instruction since the tail zeros are the same as the ;; leading zeros of the reversed value. - +(attr rule ctz_8 (veri priority)) (rule ctz_8 (lower (has_type $I8 (ctz _ x))) (a64_clz $I32 (orr_imm $I32 (rbit $I32 x) (u64_into_imm_logic $I32 0x800000)))) +(attr rule ctz_16 (veri priority)) (rule ctz_16 (lower (has_type $I16 (ctz _ x))) (a64_clz $I32 (orr_imm $I32 (rbit $I32 x) (u64_into_imm_logic $I32 0x8000)))) @@ -2005,10 +1967,11 @@ (a64_clz ty (rbit ty x))) ;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - +(attr rule cls_8 (veri priority)) (rule cls_8 (lower (has_type $I8 (cls _ x))) (sub_imm $I32 (a64_cls $I32 (put_in_reg_sext32 x)) (u8_into_imm12 24))) +(attr rule cls_16 (veri priority)) (rule cls_16 (lower (has_type $I16 (cls _ x))) (sub_imm $I32 (a64_cls $I32 (put_in_reg_sext32 x)) (u8_into_imm12 16))) @@ -2079,31 +2042,32 @@ ;; if ty == i128: ;; mov out_hi, 0 -(rule popcnt_8 (lower (has_type $I8 (popcnt _ x))) +(rule popcnt_8 (lower (popcnt $I8 x)) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size32))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8)))) (mov_from_vec nbits 0 (ScalarSize.Size8)))) ;; Note that this uses `addp` instead of `addv` as it's usually cheaper. -(rule popcnt_16 (lower (has_type $I16 (popcnt _ x))) +(rule popcnt_16 (lower (popcnt $I16 x)) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size32))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8))) (added Reg (addp nbits nbits (VectorSize.Size8x8)))) (mov_from_vec added 0 (ScalarSize.Size8)))) -(rule popcnt_32 (lower (has_type $I32 (popcnt _ x))) +(rule popcnt_32 (lower (popcnt $I32 x)) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size32))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8))) (added Reg (addv nbits (VectorSize.Size8x8)))) (mov_from_vec added 0 (ScalarSize.Size8)))) -(rule popcnt_64 (lower (has_type $I64 (popcnt _ x))) +(rule popcnt_64 (lower (popcnt $I64 x)) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size64))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8))) (added Reg (addv nbits (VectorSize.Size8x8)))) (mov_from_vec added 0 (ScalarSize.Size8)))) -(rule (lower (has_type $I128 (popcnt _ x))) +(attr rule popcnt_i128 (tag i128)) +(rule popcnt_i128 (lower (popcnt $I128 x)) (let ((val ValueRegs x) (tmp_half Reg (mov_to_fpu (value_regs_get val 0) (ScalarSize.Size64))) (tmp Reg (mov_to_vec tmp_half (value_regs_get val 1) 1 (VectorSize.Size64x2))) @@ -2111,7 +2075,8 @@ (added Reg (addv nbits (VectorSize.Size8x16)))) (value_regs (mov_from_vec added 0 (ScalarSize.Size8)) (imm $I64 (ImmExtend.Zero) 0)))) -(rule (lower (has_type $I8X16 (popcnt _ x))) +(attr rule popcnt_8x16 (tag vector)) +(rule popcnt_8x16 (lower (popcnt $I8X16 x)) (vec_cnt x (VectorSize.Size8x16))) ;;;; Rules for `bitselect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2224,7 +2189,7 @@ ;;;;; Rules for `trapz`;;;;;;;;; -(rule (lower (trapz val trap_code)) +(rule lower_trapz (lower (trapz val trap_code)) (side_effect (trap_if_cond_result (cond_result_invert (is_nonzero_cmp val)) trap_code))) ;; Helper to emit a `TrapIf` instruction for the `CondResult` provided @@ -2244,7 +2209,7 @@ ;;;;; Rules for `trapnz`;;;;;;;;; -(rule (lower (trapnz val trap_code)) +(rule lower_trapnz (lower (trapnz val trap_code)) (side_effect (trap_if_cond_result (is_nonzero_cmp val) trap_code))) ;;;; Rules for `select` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2393,6 +2358,7 @@ (atomic_cas_loop addr src1 src2 ty flags)) ;;;; Rules for 'fvdemote' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(attr fvdemote (tag vector)) (rule (lower (fvdemote _ x)) (fcvtn x (ScalarSize.Size32))) @@ -2603,19 +2569,22 @@ ;;;; Rules for loads ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule load_i8_aarch64_uload8 (lower - (has_type $I8 (load _ (little_or_native_endian flags) address offset))) + (load $I8 (little_or_native_endian flags) address offset)) (aarch64_uload8 (amode $I8 address offset) flags)) (rule load_i16_aarch64_uload16 (lower - (has_type $I16 (load _ (little_or_native_endian flags) address offset))) + (load $I16 (little_or_native_endian flags) address offset)) (aarch64_uload16 (amode $I16 address offset) flags)) (rule load_i32_aarch64_uload32 (lower - (has_type $I32 (load _ (little_or_native_endian flags) address offset))) + (load $I32 (little_or_native_endian flags) address offset)) (aarch64_uload32 (amode $I32 address offset) flags)) (rule load_i64_aarch64_uload64 (lower - (has_type $I64 (load _ (little_or_native_endian flags) address offset))) + (load $I64 (little_or_native_endian flags) address offset)) (aarch64_uload64 (amode $I64 address offset) flags)) -(rule (lower - (has_type $I128 (load _ (little_or_native_endian flags) address offset))) + +(attr rule load_i128 (tag i128)) +(attr rule load_i128 (tag vector)) +(rule load_i128 (lower + (load $I128 (little_or_native_endian flags) address offset)) (aarch64_loadp64 (pair_amode address offset) flags)) (rule -1 (lower (has_type (ty_float_or_vec (ty_16 _)) (load _ (little_or_native_endian flags) address offset))) @@ -2626,14 +2595,20 @@ (rule -3 (lower (has_type (ty_float_or_vec (ty_64 _)) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload64 (amode $F64 address offset) flags)) -(rule -4 (lower + +(attr rule load_ty_vec128 (tag vector)) +(rule load_ty_vec128 -4 (lower (has_type (ty_float_or_vec (ty_128 _)) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload128 (amode $F128 address offset) flags)) -(rule -5 (lower + +(attr rule load_ty_dyn_vec64 (tag vector)) +(rule load_ty_dyn_vec64 -5 (lower (has_type (ty_dyn_vec64 _) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload64 (amode $F64 address offset) flags)) -(rule -6 (lower + +(attr rule load_ty_dyn_vec128 (tag vector)) +(rule load_ty_dyn_vec128 -6 (lower (has_type (ty_dyn_vec128 _) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload128 (amode $I8X16 address offset) flags)) @@ -2726,7 +2701,7 @@ (side_effect (aarch64_store32 (amode $I32 address offset) flags value))) -(rule (lower +(rule store_i128 (lower (store (little_or_native_endian flags) value @ (value_type $I128) address offset)) (side_effect (aarch64_storep64 (pair_amode address offset) flags @@ -2774,11 +2749,13 @@ x) ; I128 => SIMD&FP -(rule 6 (lower (has_type (ty_float_or_vec _) (bitcast _ _ x @ (value_type $I128)))) +(attr rule bitcast_i128_simdfp (tag i128)) +(rule bitcast_i128_simdfp 6 (lower (has_type (ty_float_or_vec _) (bitcast _ _ x @ (value_type $I128)))) (mov_to_vec (mov_to_fpu (value_regs_get x 0) (ScalarSize.Size64)) (value_regs_get x 1) 1 (VectorSize.Size64x2))) ; SIMD&FP => I128 -(rule 5 (lower (has_type $I128 (bitcast _ _ x @ (value_type (ty_float_or_vec _))))) +(attr rule bitcast_simdfp_i128 (tag i128)) +(rule bitcast_simdfp_i128 5 (lower (has_type $I128 (bitcast _ _ x @ (value_type (ty_float_or_vec _))))) (value_regs (mov_from_vec x 0 (ScalarSize.Size64)) (mov_from_vec x 1 (ScalarSize.Size64)))) ; GPR => SIMD&FP @@ -2796,7 +2773,9 @@ (if (ty_int_ref_scalar_64 out_ty)) (if (ty_int_ref_scalar_64 in_ty)) x) -(rule 0 (lower (has_type $I128 (bitcast _ _ x @ (value_type $I128)))) x) + +(attr rule bitcast_i128 (tag i128)) +(rule bitcast_i128 0 (lower (has_type $I128 (bitcast _ _ x @ (value_type $I128)))) x) ;;; Rules for `extractlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3170,7 +3149,7 @@ ;;; Rules for `brif` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `brif` base case -(rule (lower_branch (brif val _ _) (two_targets taken not_taken)) +(rule lower_brif (lower_branch (brif val _ _) (two_targets taken not_taken)) (emit_side_effect (br_cond_result (is_nonzero_cmp val) taken not_taken))) ;; Helper to emit a branching instruction based on a `CondResult` @@ -3225,7 +3204,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ block (u64_from_imm64 idx))) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (a64_label_address succ_label))) @@ -3234,3 +3213,19 @@ (rule (lower (sequence_point)) (side_effect (a64_sequence_point))) + +;;;; Additional verification terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(attr aarch64_fpustore32 (veri chain)) +(attr aarch64_fpustore32 (veri chain)) + +(attr fcvt_to_sint_sat (tag vector)) +(attr extractlane (tag vector)) + +(attr rule lower_shl128 (tag i128)) +(attr rule lower_ushr128 (tag i128)) +(attr rule lower_sshr128 (tag i128)) +(attr rule lower_clz128 (tag i128)) + +(attr load_ext_name (tag TODO)) +(attr get_exception_handler_address (tag TODO)) diff --git a/cranelift/codegen/src/isa/aarch64/spec/alu_rr_imm12.isle b/cranelift/codegen/src/isa/aarch64/spec/alu_rr_imm12.isle new file mode 100644 index 000000000000..35a1a6698729 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/alu_rr_imm12.isle @@ -0,0 +1,349 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.AluRRImm12 (tag isaspec_generated)) + +(spec + (MInst.AluRRImm12 alu_op size rd rn imm12) + (provide + (match + size + ((Size64) + (match + alu_op + ((Add) + (and + (=> (not (:shift12 imm12)) (= rd (bvadd rn (zero_ext 64 (extract 11 0 (:bits imm12)))))) + (=> (:shift12 imm12) (= rd (bvadd rn (zero_ext 64 (concat (extract 11 0 (:bits imm12)) #x000))))) + ) + ) + ((Sub) + (and + (=> + (not (:shift12 imm12)) + (= rd (bvadd (bvadd rn (bvnot (zero_ext 64 (extract 11 0 (:bits imm12))))) #x0000000000000001)) + ) + (=> + (:shift12 imm12) + (= rd (bvadd (bvadd rn (bvnot (zero_ext 64 (concat (extract 11 0 (:bits imm12)) #x000)))) #x0000000000000001)) + ) + ) + ) + ((AddS) + (and + (=> + (not (:shift12 imm12)) + (with + (t1) + (and + (= t1 (zero_ext 64 (extract 11 0 (:bits imm12)))) + (= + (:V (:flags_out result)) + (bvnot + (if (= (sign_ext 128 (bvadd rn t1)) (bvadd (sign_ext 128 rn) (zero_ext 128 (extract 11 0 (:bits imm12))))) #b1 #b0) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= (zero_ext 128 (bvadd rn t1)) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd rn t1) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd rn t1))) + (= rd (bvadd rn t1)) + ) + ) + ) + (=> + (:shift12 imm12) + (with + (t1 t2) + (and + (= t1 (concat (extract 11 0 (:bits imm12)) #x000)) + (= t2 (zero_ext 64 (concat (extract 11 0 (:bits imm12)) #x000))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 128 (bvadd rn t2)) (bvadd (sign_ext 128 rn) (zero_ext 128 t1))) #b1 #b0)) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= (zero_ext 128 (bvadd rn t2)) (bvadd (zero_ext 128 rn) (zero_ext 128 t2))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd rn t2) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd rn t2))) + (= rd (bvadd rn t2)) + ) + ) + ) + ) + ) + ((SubS) + (and + (=> + (not (:shift12 imm12)) + (with + (t1 t3) + (and + (= t1 (zero_ext 64 (extract 11 0 (:bits imm12)))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 11 0 (:bits imm12)))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + (=> + (:shift12 imm12) + (with + (t1 t3) + (and + (= t1 (concat (extract 11 0 (:bits imm12)) #x000)) + (= t3 (bvadd rn (bvnot (zero_ext 64 (concat (extract 11 0 (:bits imm12)) #x000))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot (zero_ext 64 t1)))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot (zero_ext 64 t1)))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ) + ) + ) + ) + ((Size32) + (match + alu_op + ((Add) + (and + (=> (not (:shift12 imm12)) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (extract 11 0 (:bits imm12))))))) + (=> + (:shift12 imm12) + (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (concat (extract 11 0 (:bits imm12)) #x000))))) + ) + ) + ) + ((Sub) + (and + (=> + (not (:shift12 imm12)) + (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 11 0 (:bits imm12))))) #x00000001))) + ) + (=> + (:shift12 imm12) + (= + rd + (zero_ext + 64 + (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (concat (extract 11 0 (:bits imm12)) #x000)))) #x00000001) + ) + ) + ) + ) + ) + ((AddS) + (and + (=> + (not (:shift12 imm12)) + (with + (t1 t3) + (and + (= t1 (zero_ext 32 (extract 11 0 (:bits imm12)))) + (= t3 (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (extract 11 0 (:bits imm12)))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd (extract 31 0 rn) t1)) + (bvadd (sign_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 11 0 (:bits imm12)))) + ) + #b1 + #b0 + ) + ) + ) + (= (:C (:flags_out result)) (bvnot (if (= t3 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= (bvadd (extract 31 0 rn) t1) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd (extract 31 0 rn) t1))) + (= rd t3) + ) + ) + ) + (=> + (:shift12 imm12) + (with + (t1 t2 t4) + (and + (= t1 (concat (extract 11 0 (:bits imm12)) #x000)) + (= t2 (zero_ext 32 (concat (extract 11 0 (:bits imm12)) #x000))) + (= t4 (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (concat (extract 11 0 (:bits imm12)) #x000))))) + (= + (:V (:flags_out result)) + (bvnot + (if (= (sign_ext 64 (bvadd (extract 31 0 rn) t2)) (bvadd (sign_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0) + ) + ) + (= (:C (:flags_out result)) (bvnot (if (= t4 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t2))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= (bvadd (extract 31 0 rn) t2) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd (extract 31 0 rn) t2))) + (= rd t4) + ) + ) + ) + ) + ) + ((SubS) + (and + (=> + (not (:shift12 imm12)) + (with + (t1 t3 t4) + (and + (= t1 (zero_ext 32 (extract 11 0 (:bits imm12)))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 11 0 (:bits imm12)))))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 11 0 (:bits imm12))))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + (=> + (:shift12 imm12) + (with + (t1 t3 t4) + (and + (= t1 (concat (extract 11 0 (:bits imm12)) #x000)) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (concat (extract 11 0 (:bits imm12)) #x000))))) + (= + t4 + (zero_ext + 64 + (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (concat (extract 11 0 (:bits imm12)) #x000)))) #x00000001) + ) + ) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot (zero_ext 32 t1)))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot (zero_ext 32 t1)))) #x0000000000000001)) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) + (match + alu_op + ((Add) (or (not (:shift12 imm12)) (:shift12 imm12))) + ((Sub) (or (not (:shift12 imm12)) (:shift12 imm12))) + ((AddS) (or (not (:shift12 imm12)) (:shift12 imm12))) + ((SubS) (or (not (:shift12 imm12)) (:shift12 imm12))) + ) + ) + ((Size32) + (match + alu_op + ((Add) (or (not (:shift12 imm12)) (:shift12 imm12))) + ((Sub) (or (not (:shift12 imm12)) (:shift12 imm12))) + ((AddS) (or (not (:shift12 imm12)) (:shift12 imm12))) + ((SubS) (or (not (:shift12 imm12)) (:shift12 imm12))) + ) + ) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/alu_rrr.isle b/cranelift/codegen/src/isa/aarch64/spec/alu_rrr.isle new file mode 100644 index 000000000000..e9c1da98983e --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/alu_rrr.isle @@ -0,0 +1,327 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.AluRRR (tag isaspec_generated)) + +(spec + (MInst.AluRRR alu_op size rd rn rm) + (provide + (match + size + ((Size64) + (match + alu_op + ((Add) (= rd (bvadd rn rm))) + ((Sub) (= rd (bvadd (bvadd rn (bvnot rm)) #x0000000000000001))) + ((Orr) (= rd (bvor rn rm))) + ((OrrNot) (= rd (bvor rn (bvnot rm)))) + ((And) (= rd (bvand rn rm))) + ((AndNot) (= rd (bvand rn (bvnot rm)))) + ((Eor) (= rd (bvxor rn rm))) + ((EorNot) (= rd (bvxor rn (bvnot rm)))) + ((AddS) + (with + (t2) + (and + (= t2 (bvadd rn rm)) + (= (:V (:flags_out result)) (bvnot (if (= (sign_ext 128 t2) (bvadd (sign_ext 128 rn) (sign_ext 128 rm))) #b1 #b0))) + (= (:C (:flags_out result)) (bvnot (if (= (zero_ext 128 t2) (bvadd (zero_ext 128 rn) (zero_ext 128 rm))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t2 #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 t2)) + (= rd t2) + ) + ) + ) + ((SubS) + (with + (t1 t3) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((SMulH) (= rd (extract 127 64 (bvmul (sign_ext 128 rn) (sign_ext 128 rm))))) + ((UMulH) (= rd (extract 127 64 (bvmul (zero_ext 128 rn) (zero_ext 128 rm))))) + ((SDiv) + (with + (t1 t2 t4) + (and + (= t1 (= rm #x0000000000000000)) + (if + t1 + (= t2 #x0000000000000000) + (= + t4 + (extract + 63 + 0 + (if + (and (= rn #x8000000000000000) (= rm #xffffffffffffffff)) + #x00000000000000008000000000000000 + (sign_ext 128 (bvsdiv rn rm)) + ) + ) + ) + ) + (= rd (if t1 t2 t4)) + ) + ) + ) + ((UDiv) + (with + (t1 t2 t4) + (and + (= t1 (= rm #x0000000000000000)) + (if t1 (= t2 #x0000000000000000) (= t4 (extract 63 0 (bvsdiv (zero_ext 128 rn) (zero_ext 128 rm))))) + (= rd (if t1 t2 t4)) + ) + ) + ) + ((Extr) + (with + (t1 t3 t4 t5) + (and + (= t1 (= (extract 5 0 rm) #b000000)) + (if + t1 + (= t3 rn) + (and + (not (= (extract 5 0 rm) #b000000)) + (= + t4 + (bvor (bvlshr rn (zero_ext 64 (extract 5 0 rm))) (bvshl rn (zero_ext 64 (bvsub #x40 (zero_ext 8 (extract 5 0 rm)))))) + ) + ) + ) + (= t5 (if t1 t3 t4)) + (= rd t5) + ) + ) + ) + ((Lsr) (= rd (bvlshr rn (zero_ext 64 (extract 5 0 rm))))) + ((Asr) (= rd (bvashr rn (zero_ext 64 (extract 5 0 rm))))) + ((Lsl) (= rd (bvshl rn (zero_ext 64 (extract 5 0 rm))))) + ((Adc) (= rd (bvadd (bvadd rn rm) (zero_ext 64 (extract 0 0 (:C (:flags_in result))))))) + ) + ) + ((Size32) + (match + alu_op + ((Add) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (extract 31 0 rm))))) + ((Sub) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm))) #x00000001)))) + ((Orr) (= rd (zero_ext 64 (bvor (extract 31 0 rn) (extract 31 0 rm))))) + ((OrrNot) (= rd (zero_ext 64 (bvor (extract 31 0 rn) (bvnot (extract 31 0 rm)))))) + ((And) (= rd (zero_ext 64 (bvand (extract 31 0 rn) (extract 31 0 rm))))) + ((AndNot) (= rd (zero_ext 64 (bvand (extract 31 0 rn) (bvnot (extract 31 0 rm)))))) + ((Eor) (= rd (zero_ext 64 (bvxor (extract 31 0 rn) (extract 31 0 rm))))) + ((EorNot) (= rd (zero_ext 64 (bvxor (extract 31 0 rn) (bvnot (extract 31 0 rm)))))) + ((AddS) + (with + (t2 t3) + (and + (= t2 (bvadd (extract 31 0 rn) (extract 31 0 rm))) + (= t3 (zero_ext 64 (bvadd (extract 31 0 rn) (extract 31 0 rm)))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 64 t2) (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (extract 31 0 rm)))) #b1 #b0)) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t3 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 31 0 rm)))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= t2 #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 t2)) + (= rd t3) + ) + ) + ) + ((SubS) + (with + (t1 t3 t4) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((SDiv) + (with + (t1 t2 t4) + (and + (= t1 (= (extract 31 0 rm) #x00000000)) + (if + t1 + (= t2 #x0000000000000000) + (= + t4 + (zero_ext + 64 + (extract + 31 + 0 + (if + (and (= (extract 31 0 rn) #x80000000) (= (extract 31 0 rm) #xffffffff)) + #x0000000080000000 + (sign_ext 64 (bvsdiv (extract 31 0 rn) (extract 31 0 rm))) + ) + ) + ) + ) + ) + (= rd (if t1 t2 t4)) + ) + ) + ) + ((UDiv) + (with + (t1 t2 t4) + (and + (= t1 (= (extract 31 0 rm) #x00000000)) + (if + t1 + (= t2 #x0000000000000000) + (= t4 (zero_ext 64 (extract 31 0 (bvsdiv (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 31 0 rm)))))) + ) + (= rd (if t1 t2 t4)) + ) + ) + ) + ((Extr) + (with + (t1 t3 t4 t5) + (and + (= t1 (= (extract 4 0 rm) #b00000)) + (if + t1 + (= t3 (extract 31 0 rn)) + (and + (not (= (extract 4 0 rm) #b00000)) + (= + t4 + (bvor + (bvlshr (extract 31 0 rn) (zero_ext 32 (extract 4 0 rm))) + (bvshl (extract 31 0 rn) (zero_ext 32 (bvsub #x20 (zero_ext 8 (extract 4 0 rm))))) + ) + ) + ) + ) + (= t5 (if t1 t3 t4)) + (= rd (zero_ext 64 t5)) + ) + ) + ) + ((Lsr) (= rd (zero_ext 64 (bvlshr (extract 31 0 rn) (zero_ext 32 (extract 4 0 rm)))))) + ((Asr) (= rd (zero_ext 64 (bvashr (extract 31 0 rn) (zero_ext 32 (extract 4 0 rm)))))) + ((Lsl) (= rd (zero_ext 64 (bvshl (extract 31 0 rn) (zero_ext 32 (extract 4 0 rm)))))) + ((Adc) + (= + rd + (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (extract 31 0 rm)) (zero_ext 32 (extract 0 0 (:C (:flags_in result)))))) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) + (match + alu_op + ((Add) true) + ((Sub) true) + ((Orr) true) + ((OrrNot) true) + ((And) true) + ((AndNot) true) + ((Eor) true) + ((EorNot) true) + ((AddS) true) + ((SubS) true) + ((SMulH) true) + ((UMulH) true) + ((SDiv) true) + ((UDiv) true) + ((Extr) true) + ((Lsr) true) + ((Asr) true) + ((Lsl) true) + ((Adc) true) + ) + ) + ((Size32) + (match + alu_op + ((Add) true) + ((Sub) true) + ((Orr) true) + ((OrrNot) true) + ((And) true) + ((AndNot) true) + ((Eor) true) + ((EorNot) true) + ((AddS) true) + ((SubS) true) + ((SDiv) true) + ((UDiv) true) + ((Extr) true) + ((Lsr) true) + ((Asr) true) + ((Lsl) true) + ((Adc) true) + ) + ) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/alu_rrr_extend.isle b/cranelift/codegen/src/isa/aarch64/spec/alu_rrr_extend.isle new file mode 100644 index 000000000000..d2a1b372ba82 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/alu_rrr_extend.isle @@ -0,0 +1,1041 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.AluRRRExtend (tag isaspec_generated)) + +(spec + (MInst.AluRRRExtend alu_op size rd rn rm extendop) + (provide + (match + size + ((Size64) + (match + alu_op + ((Add) + (match + extendop + ((UXTB) (= rd (bvadd rn (zero_ext 64 (extract 7 0 rm))))) + ((UXTH) (= rd (bvadd rn (zero_ext 64 (extract 15 0 rm))))) + ((UXTW) (= rd (bvadd rn (zero_ext 64 (extract 31 0 rm))))) + ((UXTX) (= rd (bvadd rn rm))) + ((SXTB) (= rd (bvadd rn (sign_ext 64 (extract 7 0 rm))))) + ((SXTH) (= rd (bvadd rn (sign_ext 64 (extract 15 0 rm))))) + ((SXTW) (= rd (bvadd rn (sign_ext 64 (extract 31 0 rm))))) + ((SXTX) (= rd (bvadd rn (sign_ext 64 rm)))) + ) + ) + ((Sub) + (match + extendop + ((UXTB) (= rd (bvadd (bvadd rn (bvnot (zero_ext 64 (extract 7 0 rm)))) #x0000000000000001))) + ((UXTH) (= rd (bvadd (bvadd rn (bvnot (zero_ext 64 (extract 15 0 rm)))) #x0000000000000001))) + ((UXTW) (= rd (bvadd (bvadd rn (bvnot (zero_ext 64 (extract 31 0 rm)))) #x0000000000000001))) + ((UXTX) (= rd (bvadd (bvadd rn (bvnot rm)) #x0000000000000001))) + ((SXTB) (= rd (bvadd (bvadd rn (bvnot (sign_ext 64 (extract 7 0 rm)))) #x0000000000000001))) + ((SXTH) (= rd (bvadd (bvadd rn (bvnot (sign_ext 64 (extract 15 0 rm)))) #x0000000000000001))) + ((SXTW) (= rd (bvadd (bvadd rn (bvnot (sign_ext 64 (extract 31 0 rm)))) #x0000000000000001))) + ((SXTX) (= rd (bvadd (bvadd rn (bvnot (sign_ext 64 rm))) #x0000000000000001))) + ) + ) + ((AddS) + (match + extendop + ((UXTB) + (with + (t1) + (and + (= t1 (zero_ext 64 (extract 7 0 rm))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 128 (bvadd rn t1)) (bvadd (sign_ext 128 rn) (zero_ext 128 (extract 7 0 rm)))) #b1 #b0)) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= (zero_ext 128 (bvadd rn t1)) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd rn t1) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd rn t1))) + (= rd (bvadd rn t1)) + ) + ) + ) + ((UXTH) + (with + (t1) + (and + (= t1 (zero_ext 64 (extract 15 0 rm))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 128 (bvadd rn t1)) (bvadd (sign_ext 128 rn) (zero_ext 128 (extract 15 0 rm)))) #b1 #b0)) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= (zero_ext 128 (bvadd rn t1)) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd rn t1) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd rn t1))) + (= rd (bvadd rn t1)) + ) + ) + ) + ((UXTW) + (with + (t1) + (and + (= t1 (zero_ext 64 (extract 31 0 rm))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 128 (bvadd rn t1)) (bvadd (sign_ext 128 rn) (zero_ext 128 (extract 31 0 rm)))) #b1 #b0)) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= (zero_ext 128 (bvadd rn t1)) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd rn t1) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd rn t1))) + (= rd (bvadd rn t1)) + ) + ) + ) + ((UXTX) + (with + (t2) + (and + (= t2 (bvadd rn rm)) + (= (:V (:flags_out result)) (bvnot (if (= (sign_ext 128 t2) (bvadd (sign_ext 128 rn) (sign_ext 128 rm))) #b1 #b0))) + (= (:C (:flags_out result)) (bvnot (if (= (zero_ext 128 t2) (bvadd (zero_ext 128 rn) (zero_ext 128 rm))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t2 #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 t2)) + (= rd t2) + ) + ) + ) + ((SXTB) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 (extract 7 0 rm))) + (= t3 (bvadd rn (sign_ext 64 (extract 7 0 rm)))) + (= (:V (:flags_out result)) (bvnot (if (= (sign_ext 128 t3) (bvadd (sign_ext 128 rn) (sign_ext 128 t1))) #b1 #b0))) + (= (:C (:flags_out result)) (bvnot (if (= (zero_ext 128 t3) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 t3)) + (= rd t3) + ) + ) + ) + ((SXTH) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 (extract 15 0 rm))) + (= t3 (bvadd rn (sign_ext 64 (extract 15 0 rm)))) + (= (:V (:flags_out result)) (bvnot (if (= (sign_ext 128 t3) (bvadd (sign_ext 128 rn) (sign_ext 128 t1))) #b1 #b0))) + (= (:C (:flags_out result)) (bvnot (if (= (zero_ext 128 t3) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 t3)) + (= rd t3) + ) + ) + ) + ((SXTW) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 (extract 31 0 rm))) + (= t3 (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (:V (:flags_out result)) (bvnot (if (= (sign_ext 128 t3) (bvadd (sign_ext 128 rn) (sign_ext 128 t1))) #b1 #b0))) + (= (:C (:flags_out result)) (bvnot (if (= (zero_ext 128 t3) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 t3)) + (= rd t3) + ) + ) + ) + ((SXTX) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 rm)) + (= t3 (bvadd rn (sign_ext 64 rm))) + (= (:V (:flags_out result)) (bvnot (if (= (sign_ext 128 t3) (bvadd (sign_ext 128 rn) (sign_ext 128 t1))) #b1 #b0))) + (= (:C (:flags_out result)) (bvnot (if (= (zero_ext 128 t3) (bvadd (zero_ext 128 rn) (zero_ext 128 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 t3)) + (= rd t3) + ) + ) + ) + ) + ) + ((SubS) + (match + extendop + ((UXTB) + (with + (t1 t3) + (and + (= t1 (zero_ext 64 (extract 7 0 rm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 7 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((UXTH) + (with + (t1 t3) + (and + (= t1 (zero_ext 64 (extract 15 0 rm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 15 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((UXTW) + (with + (t1 t3) + (and + (= t1 (zero_ext 64 (extract 31 0 rm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 31 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((UXTX) + (with + (t1 t3) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((SXTB) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 (extract 7 0 rm))) + (= t3 (bvadd rn (bvnot (sign_ext 64 (extract 7 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((SXTH) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 (extract 15 0 rm))) + (= t3 (bvadd rn (bvnot (sign_ext 64 (extract 15 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((SXTW) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 (extract 31 0 rm))) + (= t3 (bvadd rn (bvnot (sign_ext 64 (extract 31 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ((SXTX) + (with + (t1 t3) + (and + (= t1 (sign_ext 64 rm)) + (= t3 (bvadd rn (bvnot (sign_ext 64 rm)))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 63 63 (bvadd t3 #x0000000000000001))) + (= rd (bvadd t3 #x0000000000000001)) + ) + ) + ) + ) + ) + ) + ) + ((Size32) + (match + alu_op + ((Add) + (match + extendop + ((UXTB) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (extract 7 0 rm)))))) + ((UXTH) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (extract 15 0 rm)))))) + ((UXTW) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (extract 31 0 rm))))) + ((UXTX) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (extract 31 0 rm))))) + ((SXTB) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 7 0 rm)))))) + ((SXTH) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 15 0 rm)))))) + ((SXTW) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 31 0 rm)))))) + ((SXTX) (= rd (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 31 0 rm)))))) + ) + ) + ((Sub) + (match + extendop + ((UXTB) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 7 0 rm)))) #x00000001)))) + ((UXTH) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 15 0 rm)))) #x00000001)))) + ((UXTW) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm))) #x00000001)))) + ((UXTX) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm))) #x00000001)))) + ((SXTB) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 7 0 rm)))) #x00000001)))) + ((SXTH) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 15 0 rm)))) #x00000001)))) + ((SXTW) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 31 0 rm)))) #x00000001)))) + ((SXTX) (= rd (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 31 0 rm)))) #x00000001)))) + ) + ) + ((AddS) + (match + extendop + ((UXTB) + (with + (t1 t3) + (and + (= t1 (zero_ext 32 (extract 7 0 rm))) + (= t3 (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (extract 7 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= (sign_ext 64 (bvadd (extract 31 0 rn) t1)) (bvadd (sign_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 7 0 rm)))) + #b1 + #b0 + ) + ) + ) + (= (:C (:flags_out result)) (bvnot (if (= t3 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= (bvadd (extract 31 0 rn) t1) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd (extract 31 0 rn) t1))) + (= rd t3) + ) + ) + ) + ((UXTH) + (with + (t1 t3) + (and + (= t1 (zero_ext 32 (extract 15 0 rm))) + (= t3 (zero_ext 64 (bvadd (extract 31 0 rn) (zero_ext 32 (extract 15 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= (sign_ext 64 (bvadd (extract 31 0 rn) t1)) (bvadd (sign_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 15 0 rm)))) + #b1 + #b0 + ) + ) + ) + (= (:C (:flags_out result)) (bvnot (if (= t3 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= (bvadd (extract 31 0 rn) t1) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd (extract 31 0 rn) t1))) + (= rd t3) + ) + ) + ) + ((UXTW) + (with + (t2 t3) + (and + (= t2 (bvadd (extract 31 0 rn) (extract 31 0 rm))) + (= t3 (zero_ext 64 (bvadd (extract 31 0 rn) (extract 31 0 rm)))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 64 t2) (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (extract 31 0 rm)))) #b1 #b0)) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t3 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 31 0 rm)))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= t2 #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 t2)) + (= rd t3) + ) + ) + ) + ((UXTX) + (with + (t2 t3) + (and + (= t2 (bvadd (extract 31 0 rn) (extract 31 0 rm))) + (= t3 (zero_ext 64 (bvadd (extract 31 0 rn) (extract 31 0 rm)))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 64 t2) (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (extract 31 0 rm)))) #b1 #b0)) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t3 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 31 0 rm)))) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= t2 #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 t2)) + (= rd t3) + ) + ) + ) + ((SXTB) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 7 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 7 0 rm)))) + (= t4 (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 7 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 64 t3) (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1))) #b1 #b0)) + ) + (= (:C (:flags_out result)) (bvnot (if (= t4 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 t3)) + (= rd t4) + ) + ) + ) + ((SXTH) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 15 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 15 0 rm)))) + (= t4 (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 15 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 64 t3) (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1))) #b1 #b0)) + ) + (= (:C (:flags_out result)) (bvnot (if (= t4 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 t3)) + (= rd t4) + ) + ) + ) + ((SXTW) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 31 0 rm)))) + (= t4 (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 31 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 64 t3) (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1))) #b1 #b0)) + ) + (= (:C (:flags_out result)) (bvnot (if (= t4 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 t3)) + (= rd t4) + ) + ) + ) + ((SXTX) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 31 0 rm)))) + (= t4 (zero_ext 64 (bvadd (extract 31 0 rn) (sign_ext 32 (extract 31 0 rm))))) + (= + (:V (:flags_out result)) + (bvnot (if (= (sign_ext 64 t3) (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1))) #b1 #b0)) + ) + (= (:C (:flags_out result)) (bvnot (if (= t4 (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1))) #b1 #b0))) + (= (:Z (:flags_out result)) (if (= t3 #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 t3)) + (= rd t4) + ) + ) + ) + ) + ) + ((SubS) + (match + extendop + ((UXTB) + (with + (t1 t3 t4) + (and + (= t1 (zero_ext 32 (extract 7 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 7 0 rm))))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 7 0 rm)))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((UXTH) + (with + (t1 t3 t4) + (and + (= t1 (zero_ext 32 (extract 15 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 15 0 rm))))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 15 0 rm)))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((UXTW) + (with + (t1 t3 t4) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((UXTX) + (with + (t1 t3 t4) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((SXTB) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 7 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 7 0 rm))))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 7 0 rm)))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((SXTH) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 15 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 15 0 rm))))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 15 0 rm)))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((SXTW) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 31 0 rm))))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 31 0 rm)))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ((SXTX) + (with + (t1 t3 t4) + (and + (= t1 (sign_ext 32 (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 31 0 rm))))) + (= t4 (zero_ext 64 (bvadd (bvadd (extract 31 0 rn) (bvnot (sign_ext 32 (extract 31 0 rm)))) #x00000001))) + (= + (:V (:flags_out result)) + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + (:C (:flags_out result)) + (bvnot (if (= t4 (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001)) #b1 #b0)) + ) + (= (:Z (:flags_out result)) (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= (:N (:flags_out result)) (extract 31 31 (bvadd t3 #x00000001))) + (= rd t4) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) + (match + alu_op + ((Add) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ((Sub) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ((AddS) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ((SubS) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ) + ) + ((Size32) + (match + alu_op + ((Add) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ((Sub) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ((AddS) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ((SubS) + (match + extendop + ((UXTB) true) + ((UXTH) true) + ((UXTW) true) + ((UXTX) true) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) + ) + ) + ) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/alu_rrr_shift.isle b/cranelift/codegen/src/isa/aarch64/spec/alu_rrr_shift.isle new file mode 100644 index 000000000000..ca67a87ed638 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/alu_rrr_shift.isle @@ -0,0 +1,710 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.AluRRRShift (tag isaspec_generated)) + +(spec + (MInst.AluRRRShift alu_op size rd rn rm shiftop) + (provide + (match + alu_op + ((Add) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvadd rn (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvadd (extract 31 0 rn) (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvadd rn (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvadd (extract 31 0 rn) (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvadd rn (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvadd (extract 31 0 rn) (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ) + ) + ((Sub) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvadd (bvadd rn (bvnot (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) #x0000000000000001)) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvadd + (bvadd (extract 31 0 rn) (bvnot (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + #x00000001 + ) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= + rd + (bvadd (bvadd rn (bvnot (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) #x0000000000000001) + ) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvadd + (bvadd (extract 31 0 rn) (bvnot (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + #x00000001 + ) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= + rd + (bvadd (bvadd rn (bvnot (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) #x0000000000000001) + ) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvadd + (bvadd (extract 31 0 rn) (bvnot (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + #x00000001 + ) + ) + ) + ) + ) + ) + ) + ) + ((Orr) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvor rn (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvor (extract 31 0 rn) (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvor rn (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvor (extract 31 0 rn) (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvor rn (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvor (extract 31 0 rn) (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ) + ) + ((And) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvand rn (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvand (extract 31 0 rn) (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvand rn (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvand (extract 31 0 rn) (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvand rn (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvand (extract 31 0 rn) (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ) + ) + ((Eor) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvxor rn (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvxor (extract 31 0 rn) (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvxor rn (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvxor (extract 31 0 rn) (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvxor rn (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvxor (extract 31 0 rn) (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop)))))) + ) + ) + ) + ) + ) + ) + ) + ((OrrNot) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvor rn (bvnot (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvor (extract 31 0 rn) (bvnot (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvor rn (bvnot (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvor (extract 31 0 rn) (bvnot (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvor rn (bvnot (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvor (extract 31 0 rn) (bvnot (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ) + ) + ((EorNot) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvxor rn (bvnot (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvxor (extract 31 0 rn) (bvnot (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvxor rn (bvnot (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvxor (extract 31 0 rn) (bvnot (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvxor rn (bvnot (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvxor (extract 31 0 rn) (bvnot (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ) + ) + ((AndNot) + (match + (:op shiftop) + ((Lsl) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvand rn (bvnot (bvshl rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvand (extract 31 0 rn) (bvnot (bvshl (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ((Lsr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvand rn (bvnot (bvlshr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvand (extract 31 0 rn) (bvnot (bvlshr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ((Asr) + (and + (=> + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (= rd (bvand rn (bvnot (bvashr rm (zero_ext 64 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + (=> + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + (= + rd + (zero_ext + 64 + (bvand (extract 31 0 rn) (bvnot (bvashr (extract 31 0 rm) (zero_ext 32 (extract 4 0 (extract 4 0 (:amt shiftop))))))) + ) + ) + ) + ) + ) + ) + ) + ((Extr) + (match + (:op shiftop) + ((Lsl) + (with + (t1) + (and + (= t1 (zero_ext 6 (extract 4 0 (extract 4 0 (:amt shiftop))))) + (= rd (zero_ext 64 (extract 31 0 (bvlshr (concat (extract 31 0 rn) (extract 31 0 rm)) (zero_ext 64 t1))))) + ) + ) + ) + ((Lsr) (= rd (extract 63 0 (bvlshr (concat rn rm) (zero_ext 128 (extract 5 0 (extract 5 0 (:amt shiftop)))))))) + ) + ) + ) + ) + (require + (match + alu_op + ((Add) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((Sub) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((Orr) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((And) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((Eor) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((OrrNot) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((EorNot) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((AndNot) + (match + (:op shiftop) + ((Lsl) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Lsr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ((Asr) + (or + (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00)) + (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000)) + ) + ) + ) + ) + ((Extr) + (match + (:op shiftop) + ((Lsl) (and (Size32? size) (= (extract 7 5 (:amt shiftop)) #b000))) + ((Lsr) (and (Size64? size) (= (extract 7 6 (:amt shiftop)) #b00))) + ) + ) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/alu_rrrr.isle b/cranelift/codegen/src/isa/aarch64/spec/alu_rrrr.isle new file mode 100644 index 000000000000..81262e6ed44e --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/alu_rrrr.isle @@ -0,0 +1,29 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.AluRRRR (tag isaspec_generated)) + +(spec + (MInst.AluRRRR alu_op size rd rn rm ra) + (provide + (match + size + ((Size64) (match alu_op ((MAdd) (= rd (bvadd ra (bvmul rn rm)))) ((MSub) (= rd (bvsub ra (bvmul rn rm)))))) + ((Size32) + (match + alu_op + ((MAdd) (= rd (zero_ext 64 (bvadd (extract 31 0 ra) (bvmul (extract 31 0 rn) (extract 31 0 rm)))))) + ((MSub) (= rd (zero_ext 64 (bvsub (extract 31 0 ra) (bvmul (extract 31 0 rn) (extract 31 0 rm)))))) + ((UMAddL) (= rd (bvadd ra (bvmul (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 31 0 rm)))))) + ((SMAddL) (= rd (bvadd ra (bvmul (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (extract 31 0 rm)))))) + ) + ) + ) + ) + (require + (match + size + ((Size64) (match alu_op ((MAdd) true) ((MSub) true))) + ((Size32) (match alu_op ((MAdd) true) ((MSub) true) ((UMAddL) true) ((SMAddL) true))) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/bit_rr.isle b/cranelift/codegen/src/isa/aarch64/spec/bit_rr.isle new file mode 100644 index 000000000000..d98d46f6b059 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/bit_rr.isle @@ -0,0 +1,2412 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.BitRR (tag isaspec_generated)) + +(spec + (MInst.BitRR op size rd rn) + (provide + (match + size + ((Size64) + (match + op + ((Cls) + (with + (t1 + t10 + t100 + t101 + t102 + t103 + t104 + t105 + t106 + t107 + t108 + t109 + t11 + t110 + t111 + t112 + t113 + t114 + t115 + t116 + t117 + t118 + t119 + t12 + t120 + t121 + t122 + t123 + t124 + t125 + t126 + t127 + t128 + t129 + t13 + t130 + t131 + t132 + t133 + t134 + t135 + t136 + t137 + t138 + t139 + t14 + t140 + t141 + t142 + t143 + t144 + t145 + t146 + t147 + t148 + t149 + t15 + t150 + t151 + t152 + t153 + t154 + t155 + t156 + t157 + t158 + t159 + t16 + t160 + t161 + t162 + t163 + t164 + t165 + t166 + t167 + t168 + t169 + t17 + t170 + t171 + t172 + t173 + t174 + t175 + t176 + t177 + t178 + t179 + t18 + t180 + t181 + t182 + t183 + t184 + t185 + t186 + t187 + t188 + t189 + t19 + t190 + t191 + t2 + t20 + t21 + t22 + t23 + t24 + t25 + t26 + t27 + t28 + t29 + t3 + t30 + t31 + t32 + t33 + t34 + t35 + t36 + t37 + t38 + t39 + t4 + t40 + t41 + t42 + t43 + t44 + t45 + t46 + t47 + t48 + t49 + t5 + t50 + t51 + t52 + t53 + t54 + t55 + t56 + t57 + t58 + t59 + t6 + t60 + t61 + t62 + t63 + t64 + t65 + t66 + t67 + t68 + t69 + t7 + t70 + t71 + t72 + t73 + t74 + t75 + t76 + t77 + t78 + t79 + t8 + t80 + t81 + t82 + t83 + t84 + t85 + t86 + t87 + t88 + t89 + t9 + t90 + t91 + t92 + t93 + t94 + t95 + t96 + t97 + t98 + t99 + ) + (and + (= t1 (bvxor (extract 63 1 (as rn (bv 64))) (extract 62 0 (as rn (bv 64))))) + (= t2 (= (extract 62 62 t1) #b1)) + (if + t2 + (= t3 #x003e) + (and + (= t4 (= (extract 61 61 (extract 61 0 t1)) #b1)) + (if + t4 + (= t5 #x003d) + (and + (= t6 (= (extract 60 60 (extract 60 0 t1)) #b1)) + (if + t6 + (= t7 #x003c) + (and + (= t8 (= (extract 59 59 (extract 59 0 t1)) #b1)) + (if + t8 + (= t9 #x003b) + (and + (= t10 (= (extract 58 58 (extract 58 0 t1)) #b1)) + (if + t10 + (= t11 #x003a) + (and + (= t12 (= (extract 57 57 (extract 57 0 t1)) #b1)) + (if + t12 + (= t13 #x0039) + (and + (= t14 (= (extract 56 56 (extract 56 0 t1)) #b1)) + (if + t14 + (= t15 #x0038) + (and + (= t16 (= (extract 55 55 (extract 55 0 t1)) #b1)) + (if + t16 + (= t17 #x0037) + (and + (= t18 (= (extract 54 54 (extract 54 0 t1)) #b1)) + (if + t18 + (= t19 #x0036) + (and + (= t20 (= (extract 53 53 (extract 53 0 t1)) #b1)) + (if + t20 + (= t21 #x0035) + (and + (= t22 (= (extract 52 52 (extract 52 0 t1)) #b1)) + (if + t22 + (= t23 #x0034) + (and + (= t24 (= (extract 51 51 (extract 51 0 t1)) #b1)) + (if + t24 + (= t25 #x0033) + (and + (= t26 (= (extract 50 50 (extract 50 0 t1)) #b1)) + (if + t26 + (= t27 #x0032) + (and + (= t28 (= (extract 49 49 (extract 49 0 t1)) #b1)) + (if + t28 + (= t29 #x0031) + (and + (= t30 (= (extract 48 48 (extract 48 0 t1)) #b1)) + (if + t30 + (= t31 #x0030) + (and + (= t32 (= (extract 47 47 (extract 47 0 t1)) #b1)) + (if + t32 + (= t33 #x002f) + (and + (= t34 (= (extract 46 46 (extract 46 0 t1)) #b1)) + (if + t34 + (= t35 #x002e) + (and + (= t36 (= (extract 45 45 (extract 45 0 t1)) #b1)) + (if + t36 + (= t37 #x002d) + (and + (= t38 (= (extract 44 44 (extract 44 0 t1)) #b1)) + (if + t38 + (= t39 #x002c) + (and + (= t40 (= (extract 43 43 (extract 43 0 t1)) #b1)) + (if + t40 + (= t41 #x002b) + (and + (= t42 (= (extract 42 42 (extract 42 0 t1)) #b1)) + (if + t42 + (= t43 #x002a) + (and + (= t44 (= (extract 41 41 (extract 41 0 t1)) #b1)) + (if + t44 + (= t45 #x0029) + (and + (= t46 (= (extract 40 40 (extract 40 0 t1)) #b1)) + (if + t46 + (= t47 #x0028) + (and + (= t48 (= (extract 39 39 (extract 39 0 t1)) #b1)) + (if + t48 + (= t49 #x0027) + (and + (= t50 (= (extract 38 38 (extract 38 0 t1)) #b1)) + (if + t50 + (= t51 #x0026) + (and + (= t52 (= (extract 37 37 (extract 37 0 t1)) #b1)) + (if + t52 + (= t53 #x0025) + (and + (= t54 (= (extract 36 36 (extract 36 0 t1)) #b1)) + (if + t54 + (= t55 #x0024) + (and + (= t56 (= (extract 35 35 (extract 35 0 t1)) #b1)) + (if + t56 + (= t57 #x0023) + (and + (= t58 (= (extract 34 34 (extract 34 0 t1)) #b1)) + (if + t58 + (= t59 #x0022) + (and + (= t60 (= (extract 33 33 (extract 33 0 t1)) #b1)) + (if + t60 + (= t61 #x0021) + (and + (= t62 (= (extract 32 32 (extract 32 0 t1)) #b1)) + (if + t62 + (= t63 #x0020) + (and + (= t64 (= (extract 31 31 (extract 31 0 t1)) #b1)) + (if + t64 + (= t65 #x001f) + (and + (= t66 (= (extract 30 30 (extract 30 0 t1)) #b1)) + (if + t66 + (= t67 #x001e) + (and + (= t68 (= (extract 29 29 (extract 29 0 t1)) #b1)) + (if + t68 + (= t69 #x001d) + (and + (= t70 (= (extract 28 28 (extract 28 0 t1)) #b1)) + (if + t70 + (= t71 #x001c) + (and + (= t72 (= (extract 27 27 (extract 27 0 t1)) #b1)) + (if + t72 + (= t73 #x001b) + (and + (= t74 (= (extract 26 26 (extract 26 0 t1)) #b1)) + (if + t74 + (= t75 #x001a) + (and + (= t76 (= (extract 25 25 (extract 25 0 t1)) #b1)) + (if + t76 + (= t77 #x0019) + (and + (= t78 (= (extract 24 24 (extract 24 0 t1)) #b1)) + (if + t78 + (= t79 #x0018) + (and + (= t80 (= (extract 23 23 (extract 23 0 t1)) #b1)) + (if + t80 + (= t81 #x0017) + (and + (= t82 (= (extract 22 22 (extract 22 0 t1)) #b1)) + (if + t82 + (= t83 #x0016) + (and + (= t84 (= (extract 21 21 (extract 21 0 t1)) #b1)) + (if + t84 + (= t85 #x0015) + (and + (= t86 (= (extract 20 20 (extract 20 0 t1)) #b1)) + (if + t86 + (= t87 #x0014) + (and + (= t88 (= (extract 19 19 (extract 19 0 t1)) #b1)) + (if + t88 + (= t89 #x0013) + (and + (= t90 (= (extract 18 18 (extract 18 0 t1)) #b1)) + (if + t90 + (= t91 #x0012) + (and + (= t92 (= (extract 17 17 (extract 17 0 t1)) #b1)) + (if + t92 + (= t93 #x0011) + (and + (= t94 (= (extract 16 16 (extract 16 0 t1)) #b1)) + (if + t94 + (= t95 #x0010) + (and + (= t96 (= (extract 15 15 (extract 15 0 t1)) #b1)) + (if + t96 + (= t97 #x000f) + (and + (= t98 (= (extract 14 14 (extract 14 0 t1)) #b1)) + (if + t98 + (= t99 #x000e) + (and + (= t100 (= (extract 13 13 (extract 13 0 t1)) #b1)) + (if + t100 + (= t101 #x000d) + (and + (= t102 (= (extract 12 12 (extract 12 0 t1)) #b1)) + (if + t102 + (= t103 #x000c) + (and + (= t104 (= (extract 11 11 (extract 11 0 t1)) #b1)) + (if + t104 + (= t105 #x000b) + (and + (= t106 (= (extract 10 10 (extract 10 0 t1)) #b1)) + (if + t106 + (= t107 #x000a) + (and + (= t108 (= (extract 9 9 (extract 9 0 t1)) #b1)) + (if + t108 + (= t109 #x0009) + (and + (= t110 (= (extract 8 8 (extract 8 0 t1)) #b1)) + (if + t110 + (= t111 #x0008) + (and + (= t112 (= (extract 7 7 (extract 7 0 t1)) #b1)) + (if + t112 + (= t113 #x0007) + (and + (= t114 (= (extract 6 6 (extract 6 0 t1)) #b1)) + (if + t114 + (= t115 #x0006) + (and + (= t116 (= (extract 5 5 (extract 5 0 t1)) #b1)) + (if + t116 + (= t117 #x0005) + (and + (= t118 (= (extract 4 4 (extract 4 0 t1)) #b1)) + (if + t118 + (= t119 #x0004) + (and + (= t120 (= (extract 3 3 (extract 3 0 t1)) #b1)) + (if + t120 + (= t121 #x0003) + (and + (= t122 (= (extract 2 2 (extract 2 0 t1)) #b1)) + (if + t122 + (= t123 #x0002) + (and + (= t124 (= (extract 1 1 (extract 1 0 t1)) #b1)) + (if + t124 + (= t125 #x0001) + (and (= t126 (= (extract 0 0 t1) #b1)) (if t126 (= t127 #x0000) (= t128 #xffff)) (= t129 (if t126 t127 t128))) + ) + (= t130 (if t124 t125 t129)) + ) + ) + (= t131 (if t122 t123 t130)) + ) + ) + (= t132 (if t120 t121 t131)) + ) + ) + (= t133 (if t118 t119 t132)) + ) + ) + (= t134 (if t116 t117 t133)) + ) + ) + (= t135 (if t114 t115 t134)) + ) + ) + (= t136 (if t112 t113 t135)) + ) + ) + (= t137 (if t110 t111 t136)) + ) + ) + (= t138 (if t108 t109 t137)) + ) + ) + (= t139 (if t106 t107 t138)) + ) + ) + (= t140 (if t104 t105 t139)) + ) + ) + (= t141 (if t102 t103 t140)) + ) + ) + (= t142 (if t100 t101 t141)) + ) + ) + (= t143 (if t98 t99 t142)) + ) + ) + (= t144 (if t96 t97 t143)) + ) + ) + (= t145 (if t94 t95 t144)) + ) + ) + (= t146 (if t92 t93 t145)) + ) + ) + (= t147 (if t90 t91 t146)) + ) + ) + (= t148 (if t88 t89 t147)) + ) + ) + (= t149 (if t86 t87 t148)) + ) + ) + (= t150 (if t84 t85 t149)) + ) + ) + (= t151 (if t82 t83 t150)) + ) + ) + (= t152 (if t80 t81 t151)) + ) + ) + (= t153 (if t78 t79 t152)) + ) + ) + (= t154 (if t76 t77 t153)) + ) + ) + (= t155 (if t74 t75 t154)) + ) + ) + (= t156 (if t72 t73 t155)) + ) + ) + (= t157 (if t70 t71 t156)) + ) + ) + (= t158 (if t68 t69 t157)) + ) + ) + (= t159 (if t66 t67 t158)) + ) + ) + (= t160 (if t64 t65 t159)) + ) + ) + (= t161 (if t62 t63 t160)) + ) + ) + (= t162 (if t60 t61 t161)) + ) + ) + (= t163 (if t58 t59 t162)) + ) + ) + (= t164 (if t56 t57 t163)) + ) + ) + (= t165 (if t54 t55 t164)) + ) + ) + (= t166 (if t52 t53 t165)) + ) + ) + (= t167 (if t50 t51 t166)) + ) + ) + (= t168 (if t48 t49 t167)) + ) + ) + (= t169 (if t46 t47 t168)) + ) + ) + (= t170 (if t44 t45 t169)) + ) + ) + (= t171 (if t42 t43 t170)) + ) + ) + (= t172 (if t40 t41 t171)) + ) + ) + (= t173 (if t38 t39 t172)) + ) + ) + (= t174 (if t36 t37 t173)) + ) + ) + (= t175 (if t34 t35 t174)) + ) + ) + (= t176 (if t32 t33 t175)) + ) + ) + (= t177 (if t30 t31 t176)) + ) + ) + (= t178 (if t28 t29 t177)) + ) + ) + (= t179 (if t26 t27 t178)) + ) + ) + (= t180 (if t24 t25 t179)) + ) + ) + (= t181 (if t22 t23 t180)) + ) + ) + (= t182 (if t20 t21 t181)) + ) + ) + (= t183 (if t18 t19 t182)) + ) + ) + (= t184 (if t16 t17 t183)) + ) + ) + (= t185 (if t14 t15 t184)) + ) + ) + (= t186 (if t12 t13 t185)) + ) + ) + (= t187 (if t10 t11 t186)) + ) + ) + (= t188 (if t8 t9 t187)) + ) + ) + (= t189 (if t6 t7 t188)) + ) + ) + (= t190 (if t4 t5 t189)) + ) + ) + (= t191 (if t2 t3 t190)) + (= rd (sign_ext 64 (bvsub #x003f (bvadd t191 #x0001)))) + ) + ) + ) + ((RBit) + (= + rd + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat (concat (extract 0 0 (as rn (bv 64))) (extract 1 1 (as rn (bv 64)))) (extract 2 2 (as rn (bv 64)))) + (extract 3 3 (as rn (bv 64))) + ) + (extract 4 4 (as rn (bv 64))) + ) + (extract 5 5 (as rn (bv 64))) + ) + (extract 6 6 (as rn (bv 64))) + ) + (extract 7 7 (as rn (bv 64))) + ) + (extract 8 8 (as rn (bv 64))) + ) + (extract 9 9 (as rn (bv 64))) + ) + (extract 10 10 (as rn (bv 64))) + ) + (extract 11 11 (as rn (bv 64))) + ) + (extract 12 12 (as rn (bv 64))) + ) + (extract 13 13 (as rn (bv 64))) + ) + (extract 14 14 (as rn (bv 64))) + ) + (extract 15 15 (as rn (bv 64))) + ) + (extract 16 16 (as rn (bv 64))) + ) + (extract 17 17 (as rn (bv 64))) + ) + (extract 18 18 (as rn (bv 64))) + ) + (extract 19 19 (as rn (bv 64))) + ) + (extract 20 20 (as rn (bv 64))) + ) + (extract 21 21 (as rn (bv 64))) + ) + (extract 22 22 (as rn (bv 64))) + ) + (extract 23 23 (as rn (bv 64))) + ) + (extract 24 24 (as rn (bv 64))) + ) + (extract 25 25 (as rn (bv 64))) + ) + (extract 26 26 (as rn (bv 64))) + ) + (extract 27 27 (as rn (bv 64))) + ) + (extract 28 28 (as rn (bv 64))) + ) + (extract 29 29 (as rn (bv 64))) + ) + (extract 30 30 (as rn (bv 64))) + ) + (extract 31 31 (as rn (bv 64))) + ) + (extract 32 32 (as rn (bv 64))) + ) + (extract 33 33 (as rn (bv 64))) + ) + (extract 34 34 (as rn (bv 64))) + ) + (extract 35 35 (as rn (bv 64))) + ) + (extract 36 36 (as rn (bv 64))) + ) + (extract 37 37 (as rn (bv 64))) + ) + (extract 38 38 (as rn (bv 64))) + ) + (extract 39 39 (as rn (bv 64))) + ) + (extract 40 40 (as rn (bv 64))) + ) + (extract 41 41 (as rn (bv 64))) + ) + (extract 42 42 (as rn (bv 64))) + ) + (extract 43 43 (as rn (bv 64))) + ) + (extract 44 44 (as rn (bv 64))) + ) + (extract 45 45 (as rn (bv 64))) + ) + (extract 46 46 (as rn (bv 64))) + ) + (extract 47 47 (as rn (bv 64))) + ) + (extract 48 48 (as rn (bv 64))) + ) + (extract 49 49 (as rn (bv 64))) + ) + (extract 50 50 (as rn (bv 64))) + ) + (extract 51 51 (as rn (bv 64))) + ) + (extract 52 52 (as rn (bv 64))) + ) + (extract 53 53 (as rn (bv 64))) + ) + (extract 54 54 (as rn (bv 64))) + ) + (extract 55 55 (as rn (bv 64))) + ) + (extract 56 56 (as rn (bv 64))) + ) + (extract 57 57 (as rn (bv 64))) + ) + (extract 58 58 (as rn (bv 64))) + ) + (extract 59 59 (as rn (bv 64))) + ) + (extract 60 60 (as rn (bv 64))) + ) + (extract 61 61 (as rn (bv 64))) + ) + (extract 62 62 (as rn (bv 64))) + ) + (extract 63 63 (as rn (bv 64))) + ) + ) + ) + ((Clz) + (with + (t1 + t10 + t100 + t101 + t102 + t103 + t104 + t105 + t106 + t107 + t108 + t109 + t11 + t110 + t111 + t112 + t113 + t114 + t115 + t116 + t117 + t118 + t119 + t12 + t120 + t121 + t122 + t123 + t124 + t125 + t126 + t127 + t128 + t129 + t13 + t130 + t131 + t132 + t133 + t134 + t135 + t136 + t137 + t138 + t139 + t14 + t140 + t141 + t142 + t143 + t144 + t145 + t146 + t147 + t148 + t149 + t15 + t150 + t151 + t152 + t153 + t154 + t155 + t156 + t157 + t158 + t159 + t16 + t160 + t161 + t162 + t163 + t164 + t165 + t166 + t167 + t168 + t169 + t17 + t170 + t171 + t172 + t173 + t174 + t175 + t176 + t177 + t178 + t179 + t18 + t180 + t181 + t182 + t183 + t184 + t185 + t186 + t187 + t188 + t189 + t19 + t190 + t191 + t192 + t193 + t2 + t20 + t21 + t22 + t23 + t24 + t25 + t26 + t27 + t28 + t29 + t3 + t30 + t31 + t32 + t33 + t34 + t35 + t36 + t37 + t38 + t39 + t4 + t40 + t41 + t42 + t43 + t44 + t45 + t46 + t47 + t48 + t49 + t5 + t50 + t51 + t52 + t53 + t54 + t55 + t56 + t57 + t58 + t59 + t6 + t60 + t61 + t62 + t63 + t64 + t65 + t66 + t67 + t68 + t69 + t7 + t70 + t71 + t72 + t73 + t74 + t75 + t76 + t77 + t78 + t79 + t8 + t80 + t81 + t82 + t83 + t84 + t85 + t86 + t87 + t88 + t89 + t9 + t90 + t91 + t92 + t93 + t94 + t95 + t96 + t97 + t98 + t99 + ) + (and + (= t1 (= (extract 63 63 (as rn (bv 64))) #b1)) + (if + t1 + (= t2 #x003f) + (and + (= t3 (= (extract 62 62 (as rn (bv 64))) #b1)) + (if + t3 + (= t4 #x003e) + (and + (= t5 (= (extract 61 61 (as rn (bv 64))) #b1)) + (if + t5 + (= t6 #x003d) + (and + (= t7 (= (extract 60 60 (as rn (bv 64))) #b1)) + (if + t7 + (= t8 #x003c) + (and + (= t9 (= (extract 59 59 (as rn (bv 64))) #b1)) + (if + t9 + (= t10 #x003b) + (and + (= t11 (= (extract 58 58 (as rn (bv 64))) #b1)) + (if + t11 + (= t12 #x003a) + (and + (= t13 (= (extract 57 57 (as rn (bv 64))) #b1)) + (if + t13 + (= t14 #x0039) + (and + (= t15 (= (extract 56 56 (as rn (bv 64))) #b1)) + (if + t15 + (= t16 #x0038) + (and + (= t17 (= (extract 55 55 (as rn (bv 64))) #b1)) + (if + t17 + (= t18 #x0037) + (and + (= t19 (= (extract 54 54 (as rn (bv 64))) #b1)) + (if + t19 + (= t20 #x0036) + (and + (= t21 (= (extract 53 53 (as rn (bv 64))) #b1)) + (if + t21 + (= t22 #x0035) + (and + (= t23 (= (extract 52 52 (as rn (bv 64))) #b1)) + (if + t23 + (= t24 #x0034) + (and + (= t25 (= (extract 51 51 (as rn (bv 64))) #b1)) + (if + t25 + (= t26 #x0033) + (and + (= t27 (= (extract 50 50 (as rn (bv 64))) #b1)) + (if + t27 + (= t28 #x0032) + (and + (= t29 (= (extract 49 49 (as rn (bv 64))) #b1)) + (if + t29 + (= t30 #x0031) + (and + (= t31 (= (extract 48 48 (as rn (bv 64))) #b1)) + (if + t31 + (= t32 #x0030) + (and + (= t33 (= (extract 47 47 (as rn (bv 64))) #b1)) + (if + t33 + (= t34 #x002f) + (and + (= t35 (= (extract 46 46 (as rn (bv 64))) #b1)) + (if + t35 + (= t36 #x002e) + (and + (= t37 (= (extract 45 45 (as rn (bv 64))) #b1)) + (if + t37 + (= t38 #x002d) + (and + (= t39 (= (extract 44 44 (as rn (bv 64))) #b1)) + (if + t39 + (= t40 #x002c) + (and + (= t41 (= (extract 43 43 (as rn (bv 64))) #b1)) + (if + t41 + (= t42 #x002b) + (and + (= t43 (= (extract 42 42 (as rn (bv 64))) #b1)) + (if + t43 + (= t44 #x002a) + (and + (= t45 (= (extract 41 41 (as rn (bv 64))) #b1)) + (if + t45 + (= t46 #x0029) + (and + (= t47 (= (extract 40 40 (as rn (bv 64))) #b1)) + (if + t47 + (= t48 #x0028) + (and + (= t49 (= (extract 39 39 (as rn (bv 64))) #b1)) + (if + t49 + (= t50 #x0027) + (and + (= t51 (= (extract 38 38 (as rn (bv 64))) #b1)) + (if + t51 + (= t52 #x0026) + (and + (= t53 (= (extract 37 37 (as rn (bv 64))) #b1)) + (if + t53 + (= t54 #x0025) + (and + (= t55 (= (extract 36 36 (as rn (bv 64))) #b1)) + (if + t55 + (= t56 #x0024) + (and + (= t57 (= (extract 35 35 (as rn (bv 64))) #b1)) + (if + t57 + (= t58 #x0023) + (and + (= t59 (= (extract 34 34 (as rn (bv 64))) #b1)) + (if + t59 + (= t60 #x0022) + (and + (= t61 (= (extract 33 33 (as rn (bv 64))) #b1)) + (if + t61 + (= t62 #x0021) + (and + (= t63 (= (extract 32 32 (as rn (bv 64))) #b1)) + (if + t63 + (= t64 #x0020) + (and + (= t65 (= (extract 31 31 (as rn (bv 64))) #b1)) + (if + t65 + (= t66 #x001f) + (and + (= t67 (= (extract 30 30 (as rn (bv 64))) #b1)) + (if + t67 + (= t68 #x001e) + (and + (= t69 (= (extract 29 29 (as rn (bv 64))) #b1)) + (if + t69 + (= t70 #x001d) + (and + (= t71 (= (extract 28 28 (as rn (bv 64))) #b1)) + (if + t71 + (= t72 #x001c) + (and + (= t73 (= (extract 27 27 (as rn (bv 64))) #b1)) + (if + t73 + (= t74 #x001b) + (and + (= t75 (= (extract 26 26 (as rn (bv 64))) #b1)) + (if + t75 + (= t76 #x001a) + (and + (= t77 (= (extract 25 25 (as rn (bv 64))) #b1)) + (if + t77 + (= t78 #x0019) + (and + (= t79 (= (extract 24 24 (as rn (bv 64))) #b1)) + (if + t79 + (= t80 #x0018) + (and + (= t81 (= (extract 23 23 (as rn (bv 64))) #b1)) + (if + t81 + (= t82 #x0017) + (and + (= t83 (= (extract 22 22 (as rn (bv 64))) #b1)) + (if + t83 + (= t84 #x0016) + (and + (= t85 (= (extract 21 21 (as rn (bv 64))) #b1)) + (if + t85 + (= t86 #x0015) + (and + (= t87 (= (extract 20 20 (as rn (bv 64))) #b1)) + (if + t87 + (= t88 #x0014) + (and + (= t89 (= (extract 19 19 (as rn (bv 64))) #b1)) + (if + t89 + (= t90 #x0013) + (and + (= t91 (= (extract 18 18 (as rn (bv 64))) #b1)) + (if + t91 + (= t92 #x0012) + (and + (= t93 (= (extract 17 17 (as rn (bv 64))) #b1)) + (if + t93 + (= t94 #x0011) + (and + (= t95 (= (extract 16 16 (as rn (bv 64))) #b1)) + (if + t95 + (= t96 #x0010) + (and + (= t97 (= (extract 15 15 (as rn (bv 64))) #b1)) + (if + t97 + (= t98 #x000f) + (and + (= t99 (= (extract 14 14 (as rn (bv 64))) #b1)) + (if + t99 + (= t100 #x000e) + (and + (= t101 (= (extract 13 13 (as rn (bv 64))) #b1)) + (if + t101 + (= t102 #x000d) + (and + (= t103 (= (extract 12 12 (as rn (bv 64))) #b1)) + (if + t103 + (= t104 #x000c) + (and + (= t105 (= (extract 11 11 (as rn (bv 64))) #b1)) + (if + t105 + (= t106 #x000b) + (and + (= t107 (= (extract 10 10 (as rn (bv 64))) #b1)) + (if + t107 + (= t108 #x000a) + (and + (= t109 (= (extract 9 9 (as rn (bv 64))) #b1)) + (if + t109 + (= t110 #x0009) + (and + (= t111 (= (extract 8 8 (as rn (bv 64))) #b1)) + (if + t111 + (= t112 #x0008) + (and + (= t113 (= (extract 7 7 (as rn (bv 64))) #b1)) + (if + t113 + (= t114 #x0007) + (and + (= t115 (= (extract 6 6 (as rn (bv 64))) #b1)) + (if + t115 + (= t116 #x0006) + (and + (= t117 (= (extract 5 5 (as rn (bv 64))) #b1)) + (if + t117 + (= t118 #x0005) + (and + (= t119 (= (extract 4 4 (as rn (bv 64))) #b1)) + (if + t119 + (= t120 #x0004) + (and + (= t121 (= (extract 3 3 (as rn (bv 64))) #b1)) + (if + t121 + (= t122 #x0003) + (and + (= t123 (= (extract 2 2 (as rn (bv 64))) #b1)) + (if + t123 + (= t124 #x0002) + (and + (= t125 (= (extract 1 1 (as rn (bv 64))) #b1)) + (if + t125 + (= t126 #x0001) + (and + (= t127 (= (extract 0 0 (as rn (bv 64))) #b1)) + (if t127 (= t128 #x0000) (= t129 #xffff)) + (= t130 (if t127 t128 t129)) + ) + ) + (= t131 (if t125 t126 t130)) + ) + ) + (= t132 (if t123 t124 t131)) + ) + ) + (= t133 (if t121 t122 t132)) + ) + ) + (= t134 (if t119 t120 t133)) + ) + ) + (= t135 (if t117 t118 t134)) + ) + ) + (= t136 (if t115 t116 t135)) + ) + ) + (= t137 (if t113 t114 t136)) + ) + ) + (= t138 (if t111 t112 t137)) + ) + ) + (= t139 (if t109 t110 t138)) + ) + ) + (= t140 (if t107 t108 t139)) + ) + ) + (= t141 (if t105 t106 t140)) + ) + ) + (= t142 (if t103 t104 t141)) + ) + ) + (= t143 (if t101 t102 t142)) + ) + ) + (= t144 (if t99 t100 t143)) + ) + ) + (= t145 (if t97 t98 t144)) + ) + ) + (= t146 (if t95 t96 t145)) + ) + ) + (= t147 (if t93 t94 t146)) + ) + ) + (= t148 (if t91 t92 t147)) + ) + ) + (= t149 (if t89 t90 t148)) + ) + ) + (= t150 (if t87 t88 t149)) + ) + ) + (= t151 (if t85 t86 t150)) + ) + ) + (= t152 (if t83 t84 t151)) + ) + ) + (= t153 (if t81 t82 t152)) + ) + ) + (= t154 (if t79 t80 t153)) + ) + ) + (= t155 (if t77 t78 t154)) + ) + ) + (= t156 (if t75 t76 t155)) + ) + ) + (= t157 (if t73 t74 t156)) + ) + ) + (= t158 (if t71 t72 t157)) + ) + ) + (= t159 (if t69 t70 t158)) + ) + ) + (= t160 (if t67 t68 t159)) + ) + ) + (= t161 (if t65 t66 t160)) + ) + ) + (= t162 (if t63 t64 t161)) + ) + ) + (= t163 (if t61 t62 t162)) + ) + ) + (= t164 (if t59 t60 t163)) + ) + ) + (= t165 (if t57 t58 t164)) + ) + ) + (= t166 (if t55 t56 t165)) + ) + ) + (= t167 (if t53 t54 t166)) + ) + ) + (= t168 (if t51 t52 t167)) + ) + ) + (= t169 (if t49 t50 t168)) + ) + ) + (= t170 (if t47 t48 t169)) + ) + ) + (= t171 (if t45 t46 t170)) + ) + ) + (= t172 (if t43 t44 t171)) + ) + ) + (= t173 (if t41 t42 t172)) + ) + ) + (= t174 (if t39 t40 t173)) + ) + ) + (= t175 (if t37 t38 t174)) + ) + ) + (= t176 (if t35 t36 t175)) + ) + ) + (= t177 (if t33 t34 t176)) + ) + ) + (= t178 (if t31 t32 t177)) + ) + ) + (= t179 (if t29 t30 t178)) + ) + ) + (= t180 (if t27 t28 t179)) + ) + ) + (= t181 (if t25 t26 t180)) + ) + ) + (= t182 (if t23 t24 t181)) + ) + ) + (= t183 (if t21 t22 t182)) + ) + ) + (= t184 (if t19 t20 t183)) + ) + ) + (= t185 (if t17 t18 t184)) + ) + ) + (= t186 (if t15 t16 t185)) + ) + ) + (= t187 (if t13 t14 t186)) + ) + ) + (= t188 (if t11 t12 t187)) + ) + ) + (= t189 (if t9 t10 t188)) + ) + ) + (= t190 (if t7 t8 t189)) + ) + ) + (= t191 (if t5 t6 t190)) + ) + ) + (= t192 (if t3 t4 t191)) + ) + ) + (= t193 (if t1 t2 t192)) + (= rd (sign_ext 64 (bvsub #x0040 (bvadd t193 #x0001)))) + ) + ) + ) + ) + ) + ((Size32) + (match + op + ((Cls) + (with + (t1 + t10 + t11 + t12 + t13 + t14 + t15 + t16 + t17 + t18 + t19 + t2 + t20 + t21 + t22 + t23 + t24 + t25 + t26 + t27 + t28 + t29 + t3 + t30 + t31 + t32 + t33 + t34 + t35 + t36 + t37 + t38 + t39 + t4 + t40 + t41 + t42 + t43 + t44 + t45 + t46 + t47 + t48 + t49 + t5 + t50 + t51 + t52 + t53 + t54 + t55 + t56 + t57 + t58 + t59 + t6 + t60 + t61 + t62 + t63 + t64 + t65 + t66 + t67 + t68 + t69 + t7 + t70 + t71 + t72 + t73 + t74 + t75 + t76 + t77 + t78 + t79 + t8 + t80 + t81 + t82 + t83 + t84 + t85 + t86 + t87 + t88 + t89 + t9 + t90 + t91 + t92 + t93 + t94 + t95 + ) + (and + (= t1 (bvxor (extract 31 1 (as rn (bv 64))) (extract 30 0 (as rn (bv 64))))) + (= t2 (= (extract 30 30 t1) #b1)) + (if + t2 + (= t3 #x001e) + (and + (= t4 (= (extract 29 29 (extract 29 0 t1)) #b1)) + (if + t4 + (= t5 #x001d) + (and + (= t6 (= (extract 28 28 (extract 28 0 t1)) #b1)) + (if + t6 + (= t7 #x001c) + (and + (= t8 (= (extract 27 27 (extract 27 0 t1)) #b1)) + (if + t8 + (= t9 #x001b) + (and + (= t10 (= (extract 26 26 (extract 26 0 t1)) #b1)) + (if + t10 + (= t11 #x001a) + (and + (= t12 (= (extract 25 25 (extract 25 0 t1)) #b1)) + (if + t12 + (= t13 #x0019) + (and + (= t14 (= (extract 24 24 (extract 24 0 t1)) #b1)) + (if + t14 + (= t15 #x0018) + (and + (= t16 (= (extract 23 23 (extract 23 0 t1)) #b1)) + (if + t16 + (= t17 #x0017) + (and + (= t18 (= (extract 22 22 (extract 22 0 t1)) #b1)) + (if + t18 + (= t19 #x0016) + (and + (= t20 (= (extract 21 21 (extract 21 0 t1)) #b1)) + (if + t20 + (= t21 #x0015) + (and + (= t22 (= (extract 20 20 (extract 20 0 t1)) #b1)) + (if + t22 + (= t23 #x0014) + (and + (= t24 (= (extract 19 19 (extract 19 0 t1)) #b1)) + (if + t24 + (= t25 #x0013) + (and + (= t26 (= (extract 18 18 (extract 18 0 t1)) #b1)) + (if + t26 + (= t27 #x0012) + (and + (= t28 (= (extract 17 17 (extract 17 0 t1)) #b1)) + (if + t28 + (= t29 #x0011) + (and + (= t30 (= (extract 16 16 (extract 16 0 t1)) #b1)) + (if + t30 + (= t31 #x0010) + (and + (= t32 (= (extract 15 15 (extract 15 0 t1)) #b1)) + (if + t32 + (= t33 #x000f) + (and + (= t34 (= (extract 14 14 (extract 14 0 t1)) #b1)) + (if + t34 + (= t35 #x000e) + (and + (= t36 (= (extract 13 13 (extract 13 0 t1)) #b1)) + (if + t36 + (= t37 #x000d) + (and + (= t38 (= (extract 12 12 (extract 12 0 t1)) #b1)) + (if + t38 + (= t39 #x000c) + (and + (= t40 (= (extract 11 11 (extract 11 0 t1)) #b1)) + (if + t40 + (= t41 #x000b) + (and + (= t42 (= (extract 10 10 (extract 10 0 t1)) #b1)) + (if + t42 + (= t43 #x000a) + (and + (= t44 (= (extract 9 9 (extract 9 0 t1)) #b1)) + (if + t44 + (= t45 #x0009) + (and + (= t46 (= (extract 8 8 (extract 8 0 t1)) #b1)) + (if + t46 + (= t47 #x0008) + (and + (= t48 (= (extract 7 7 (extract 7 0 t1)) #b1)) + (if + t48 + (= t49 #x0007) + (and + (= t50 (= (extract 6 6 (extract 6 0 t1)) #b1)) + (if + t50 + (= t51 #x0006) + (and + (= t52 (= (extract 5 5 (extract 5 0 t1)) #b1)) + (if + t52 + (= t53 #x0005) + (and + (= t54 (= (extract 4 4 (extract 4 0 t1)) #b1)) + (if + t54 + (= t55 #x0004) + (and + (= t56 (= (extract 3 3 (extract 3 0 t1)) #b1)) + (if + t56 + (= t57 #x0003) + (and + (= t58 (= (extract 2 2 (extract 2 0 t1)) #b1)) + (if + t58 + (= t59 #x0002) + (and + (= t60 (= (extract 1 1 (extract 1 0 t1)) #b1)) + (if + t60 + (= t61 #x0001) + (and (= t62 (= (extract 0 0 t1) #b1)) (if t62 (= t63 #x0000) (= t64 #xffff)) (= t65 (if t62 t63 t64))) + ) + (= t66 (if t60 t61 t65)) + ) + ) + (= t67 (if t58 t59 t66)) + ) + ) + (= t68 (if t56 t57 t67)) + ) + ) + (= t69 (if t54 t55 t68)) + ) + ) + (= t70 (if t52 t53 t69)) + ) + ) + (= t71 (if t50 t51 t70)) + ) + ) + (= t72 (if t48 t49 t71)) + ) + ) + (= t73 (if t46 t47 t72)) + ) + ) + (= t74 (if t44 t45 t73)) + ) + ) + (= t75 (if t42 t43 t74)) + ) + ) + (= t76 (if t40 t41 t75)) + ) + ) + (= t77 (if t38 t39 t76)) + ) + ) + (= t78 (if t36 t37 t77)) + ) + ) + (= t79 (if t34 t35 t78)) + ) + ) + (= t80 (if t32 t33 t79)) + ) + ) + (= t81 (if t30 t31 t80)) + ) + ) + (= t82 (if t28 t29 t81)) + ) + ) + (= t83 (if t26 t27 t82)) + ) + ) + (= t84 (if t24 t25 t83)) + ) + ) + (= t85 (if t22 t23 t84)) + ) + ) + (= t86 (if t20 t21 t85)) + ) + ) + (= t87 (if t18 t19 t86)) + ) + ) + (= t88 (if t16 t17 t87)) + ) + ) + (= t89 (if t14 t15 t88)) + ) + ) + (= t90 (if t12 t13 t89)) + ) + ) + (= t91 (if t10 t11 t90)) + ) + ) + (= t92 (if t8 t9 t91)) + ) + ) + (= t93 (if t6 t7 t92)) + ) + ) + (= t94 (if t4 t5 t93)) + ) + ) + (= t95 (if t2 t3 t94)) + (= rd (zero_ext 64 (sign_ext 32 (bvsub #x001f (bvadd t95 #x0001))))) + ) + ) + ) + ((RBit) + (= + rd + (zero_ext + 64 + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat + (concat (concat (extract 0 0 (as rn (bv 64))) (extract 1 1 (as rn (bv 64)))) (extract 2 2 (as rn (bv 64)))) + (extract 3 3 (as rn (bv 64))) + ) + (extract 4 4 (as rn (bv 64))) + ) + (extract 5 5 (as rn (bv 64))) + ) + (extract 6 6 (as rn (bv 64))) + ) + (extract 7 7 (as rn (bv 64))) + ) + (extract 8 8 (as rn (bv 64))) + ) + (extract 9 9 (as rn (bv 64))) + ) + (extract 10 10 (as rn (bv 64))) + ) + (extract 11 11 (as rn (bv 64))) + ) + (extract 12 12 (as rn (bv 64))) + ) + (extract 13 13 (as rn (bv 64))) + ) + (extract 14 14 (as rn (bv 64))) + ) + (extract 15 15 (as rn (bv 64))) + ) + (extract 16 16 (as rn (bv 64))) + ) + (extract 17 17 (as rn (bv 64))) + ) + (extract 18 18 (as rn (bv 64))) + ) + (extract 19 19 (as rn (bv 64))) + ) + (extract 20 20 (as rn (bv 64))) + ) + (extract 21 21 (as rn (bv 64))) + ) + (extract 22 22 (as rn (bv 64))) + ) + (extract 23 23 (as rn (bv 64))) + ) + (extract 24 24 (as rn (bv 64))) + ) + (extract 25 25 (as rn (bv 64))) + ) + (extract 26 26 (as rn (bv 64))) + ) + (extract 27 27 (as rn (bv 64))) + ) + (extract 28 28 (as rn (bv 64))) + ) + (extract 29 29 (as rn (bv 64))) + ) + (extract 30 30 (as rn (bv 64))) + ) + (extract 31 31 (as rn (bv 64))) + ) + ) + ) + ) + ((Clz) + (with + (t1 + t10 + t11 + t12 + t13 + t14 + t15 + t16 + t17 + t18 + t19 + t2 + t20 + t21 + t22 + t23 + t24 + t25 + t26 + t27 + t28 + t29 + t3 + t30 + t31 + t32 + t33 + t34 + t35 + t36 + t37 + t38 + t39 + t4 + t40 + t41 + t42 + t43 + t44 + t45 + t46 + t47 + t48 + t49 + t5 + t50 + t51 + t52 + t53 + t54 + t55 + t56 + t57 + t58 + t59 + t6 + t60 + t61 + t62 + t63 + t64 + t65 + t66 + t67 + t68 + t69 + t7 + t70 + t71 + t72 + t73 + t74 + t75 + t76 + t77 + t78 + t79 + t8 + t80 + t81 + t82 + t83 + t84 + t85 + t86 + t87 + t88 + t89 + t9 + t90 + t91 + t92 + t93 + t94 + t95 + t96 + t97 + ) + (and + (= t1 (= (extract 31 31 (as rn (bv 64))) #b1)) + (if + t1 + (= t2 #x001f) + (and + (= t3 (= (extract 30 30 (as rn (bv 64))) #b1)) + (if + t3 + (= t4 #x001e) + (and + (= t5 (= (extract 29 29 (as rn (bv 64))) #b1)) + (if + t5 + (= t6 #x001d) + (and + (= t7 (= (extract 28 28 (as rn (bv 64))) #b1)) + (if + t7 + (= t8 #x001c) + (and + (= t9 (= (extract 27 27 (as rn (bv 64))) #b1)) + (if + t9 + (= t10 #x001b) + (and + (= t11 (= (extract 26 26 (as rn (bv 64))) #b1)) + (if + t11 + (= t12 #x001a) + (and + (= t13 (= (extract 25 25 (as rn (bv 64))) #b1)) + (if + t13 + (= t14 #x0019) + (and + (= t15 (= (extract 24 24 (as rn (bv 64))) #b1)) + (if + t15 + (= t16 #x0018) + (and + (= t17 (= (extract 23 23 (as rn (bv 64))) #b1)) + (if + t17 + (= t18 #x0017) + (and + (= t19 (= (extract 22 22 (as rn (bv 64))) #b1)) + (if + t19 + (= t20 #x0016) + (and + (= t21 (= (extract 21 21 (as rn (bv 64))) #b1)) + (if + t21 + (= t22 #x0015) + (and + (= t23 (= (extract 20 20 (as rn (bv 64))) #b1)) + (if + t23 + (= t24 #x0014) + (and + (= t25 (= (extract 19 19 (as rn (bv 64))) #b1)) + (if + t25 + (= t26 #x0013) + (and + (= t27 (= (extract 18 18 (as rn (bv 64))) #b1)) + (if + t27 + (= t28 #x0012) + (and + (= t29 (= (extract 17 17 (as rn (bv 64))) #b1)) + (if + t29 + (= t30 #x0011) + (and + (= t31 (= (extract 16 16 (as rn (bv 64))) #b1)) + (if + t31 + (= t32 #x0010) + (and + (= t33 (= (extract 15 15 (as rn (bv 64))) #b1)) + (if + t33 + (= t34 #x000f) + (and + (= t35 (= (extract 14 14 (as rn (bv 64))) #b1)) + (if + t35 + (= t36 #x000e) + (and + (= t37 (= (extract 13 13 (as rn (bv 64))) #b1)) + (if + t37 + (= t38 #x000d) + (and + (= t39 (= (extract 12 12 (as rn (bv 64))) #b1)) + (if + t39 + (= t40 #x000c) + (and + (= t41 (= (extract 11 11 (as rn (bv 64))) #b1)) + (if + t41 + (= t42 #x000b) + (and + (= t43 (= (extract 10 10 (as rn (bv 64))) #b1)) + (if + t43 + (= t44 #x000a) + (and + (= t45 (= (extract 9 9 (as rn (bv 64))) #b1)) + (if + t45 + (= t46 #x0009) + (and + (= t47 (= (extract 8 8 (as rn (bv 64))) #b1)) + (if + t47 + (= t48 #x0008) + (and + (= t49 (= (extract 7 7 (as rn (bv 64))) #b1)) + (if + t49 + (= t50 #x0007) + (and + (= t51 (= (extract 6 6 (as rn (bv 64))) #b1)) + (if + t51 + (= t52 #x0006) + (and + (= t53 (= (extract 5 5 (as rn (bv 64))) #b1)) + (if + t53 + (= t54 #x0005) + (and + (= t55 (= (extract 4 4 (as rn (bv 64))) #b1)) + (if + t55 + (= t56 #x0004) + (and + (= t57 (= (extract 3 3 (as rn (bv 64))) #b1)) + (if + t57 + (= t58 #x0003) + (and + (= t59 (= (extract 2 2 (as rn (bv 64))) #b1)) + (if + t59 + (= t60 #x0002) + (and + (= t61 (= (extract 1 1 (as rn (bv 64))) #b1)) + (if + t61 + (= t62 #x0001) + (and (= t63 (= (extract 0 0 (as rn (bv 64))) #b1)) (if t63 (= t64 #x0000) (= t65 #xffff)) (= t66 (if t63 t64 t65))) + ) + (= t67 (if t61 t62 t66)) + ) + ) + (= t68 (if t59 t60 t67)) + ) + ) + (= t69 (if t57 t58 t68)) + ) + ) + (= t70 (if t55 t56 t69)) + ) + ) + (= t71 (if t53 t54 t70)) + ) + ) + (= t72 (if t51 t52 t71)) + ) + ) + (= t73 (if t49 t50 t72)) + ) + ) + (= t74 (if t47 t48 t73)) + ) + ) + (= t75 (if t45 t46 t74)) + ) + ) + (= t76 (if t43 t44 t75)) + ) + ) + (= t77 (if t41 t42 t76)) + ) + ) + (= t78 (if t39 t40 t77)) + ) + ) + (= t79 (if t37 t38 t78)) + ) + ) + (= t80 (if t35 t36 t79)) + ) + ) + (= t81 (if t33 t34 t80)) + ) + ) + (= t82 (if t31 t32 t81)) + ) + ) + (= t83 (if t29 t30 t82)) + ) + ) + (= t84 (if t27 t28 t83)) + ) + ) + (= t85 (if t25 t26 t84)) + ) + ) + (= t86 (if t23 t24 t85)) + ) + ) + (= t87 (if t21 t22 t86)) + ) + ) + (= t88 (if t19 t20 t87)) + ) + ) + (= t89 (if t17 t18 t88)) + ) + ) + (= t90 (if t15 t16 t89)) + ) + ) + (= t91 (if t13 t14 t90)) + ) + ) + (= t92 (if t11 t12 t91)) + ) + ) + (= t93 (if t9 t10 t92)) + ) + ) + (= t94 (if t7 t8 t93)) + ) + ) + (= t95 (if t5 t6 t94)) + ) + ) + (= t96 (if t3 t4 t95)) + ) + ) + (= t97 (if t1 t2 t96)) + (= rd (zero_ext 64 (sign_ext 32 (bvsub #x0020 (bvadd t97 #x0001))))) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) (match op ((Cls) true) ((RBit) true) ((Clz) true))) + ((Size32) (match op ((Cls) true) ((RBit) true) ((Clz) true))) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/conds.isle b/cranelift/codegen/src/isa/aarch64/spec/conds.isle new file mode 100644 index 000000000000..b52a1c0339b8 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/conds.isle @@ -0,0 +1,3705 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.CSel (tag isaspec_generated)) + +(spec + (MInst.CSel rd cond rn rm) + (provide + (match + cond + ((Le) + (with + (t3 t5 t7) + (and + (= t3 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if t3 (= t5 rn) (= t7 rm)) + (= rd (if t3 t5 t7)) + ) + ) + ) + ((Gt) + (with + (t3 t5 t7) + (and + (= t3 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if t3 (= t5 rn) (= t7 rm)) + (= rd (if t3 t5 t7)) + ) + ) + ) + ((Lt) + (with + (t2 t4 t6) + (and (= t2 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) (if t2 (= t4 rn) (= t6 rm)) (= rd (if t2 t4 t6))) + ) + ) + ((Ge) + (with + (t2 t4 t6) + (and (= t2 (= (:N (:flags_in result)) (:V (:flags_in result)))) (if t2 (= t4 rn) (= t6 rm)) (= rd (if t2 t4 t6))) + ) + ) + ((Ls) + (with + (t2 t4 t6) + (and + (= t2 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if t2 (= t4 rn) (= t6 rm)) + (= rd (if t2 t4 t6)) + ) + ) + ) + ((Hi) + (with + (t2 t4 t6) + (and + (= t2 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if t2 (= t4 rn) (= t6 rm)) + (= rd (if t2 t4 t6)) + ) + ) + ) + ((Vc) + (with (t1 t3 t5) (and (= t1 (not (= (:V (:flags_in result)) #b1))) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5)))) + ) + ((Vs) (with (t1 t3 t5) (and (= t1 (= (:V (:flags_in result)) #b1)) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5))))) + ((Pl) + (with (t1 t3 t5) (and (= t1 (not (= (:N (:flags_in result)) #b1))) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5)))) + ) + ((Mi) (with (t1 t3 t5) (and (= t1 (= (:N (:flags_in result)) #b1)) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5))))) + ((Lo) + (with (t1 t3 t5) (and (= t1 (not (= (:C (:flags_in result)) #b1))) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5)))) + ) + ((Hs) (with (t1 t3 t5) (and (= t1 (= (:C (:flags_in result)) #b1)) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5))))) + ((Ne) + (with (t1 t3 t5) (and (= t1 (not (= (:Z (:flags_in result)) #b1))) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5)))) + ) + ((Eq) (with (t1 t3 t5) (and (= t1 (= (:Z (:flags_in result)) #b1)) (if t1 (= t3 rn) (= t5 rm)) (= rd (if t1 t3 t5))))) + ) + ) + (require + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) +) + +(attr MInst.CSNeg (tag isaspec_generated)) + +(spec + (MInst.CSNeg rd cond rn rm) + (provide + (match + cond + ((Le) + (with + (t3 t5 t7) + (and + (= t3 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if t3 (= t5 rn) (= t7 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t3 t5 t7)) + ) + ) + ) + ((Gt) + (with + (t3 t5 t7) + (and + (= t3 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if t3 (= t5 rn) (= t7 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t3 t5 t7)) + ) + ) + ) + ((Lt) + (with + (t2 t4 t6) + (and + (= t2 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) + (if t2 (= t4 rn) (= t6 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t2 t4 t6)) + ) + ) + ) + ((Ge) + (with + (t2 t4 t6) + (and + (= t2 (= (:N (:flags_in result)) (:V (:flags_in result)))) + (if t2 (= t4 rn) (= t6 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t2 t4 t6)) + ) + ) + ) + ((Ls) + (with + (t2 t4 t6) + (and + (= t2 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if t2 (= t4 rn) (= t6 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t2 t4 t6)) + ) + ) + ) + ((Hi) + (with + (t2 t4 t6) + (and + (= t2 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if t2 (= t4 rn) (= t6 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t2 t4 t6)) + ) + ) + ) + ((Vc) + (with + (t1 t3 t5) + (and + (= t1 (not (= (:V (:flags_in result)) #b1))) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ((Vs) + (with + (t1 t3 t5) + (and + (= t1 (= (:V (:flags_in result)) #b1)) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ((Pl) + (with + (t1 t3 t5) + (and + (= t1 (not (= (:N (:flags_in result)) #b1))) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ((Mi) + (with + (t1 t3 t5) + (and + (= t1 (= (:N (:flags_in result)) #b1)) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ((Lo) + (with + (t1 t3 t5) + (and + (= t1 (not (= (:C (:flags_in result)) #b1))) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ((Hs) + (with + (t1 t3 t5) + (and + (= t1 (= (:C (:flags_in result)) #b1)) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ((Ne) + (with + (t1 t3 t5) + (and + (= t1 (not (= (:Z (:flags_in result)) #b1))) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ((Eq) + (with + (t1 t3 t5) + (and + (= t1 (= (:Z (:flags_in result)) #b1)) + (if t1 (= t3 rn) (= t5 (bvadd (bvnot rm) #x0000000000000001))) + (= rd (if t1 t3 t5)) + ) + ) + ) + ) + ) + (require + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) +) + +(attr MInst.CSet (tag isaspec_generated)) + +(spec + (MInst.CSet rd cond) + (provide + (match + cond + ((Le) + (with + (t3 t4 t5) + (and + (= t3 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if t3 (= t4 #x0000000000000000) (= t5 #x0000000000000001)) + (= rd (if t3 t4 t5)) + ) + ) + ) + ((Gt) + (with + (t3 t4 t5) + (and + (= t3 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if t3 (= t4 #x0000000000000000) (= t5 #x0000000000000001)) + (= rd (if t3 t4 t5)) + ) + ) + ) + ((Lt) + (with + (t2 t3 t4) + (and + (= t2 (= (:N (:flags_in result)) (:V (:flags_in result)))) + (if t2 (= t3 #x0000000000000000) (= t4 #x0000000000000001)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Ge) + (with + (t2 t3 t4) + (and + (= t2 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) + (if t2 (= t3 #x0000000000000000) (= t4 #x0000000000000001)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Ls) + (with + (t2 t3 t4) + (and + (= t2 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if t2 (= t3 #x0000000000000000) (= t4 #x0000000000000001)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Hi) + (with + (t2 t3 t4) + (and + (= t2 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if t2 (= t3 #x0000000000000000) (= t4 #x0000000000000001)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Vc) + (with + (t1 t2 t3) + (and + (= t1 (= (:V (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Vs) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:V (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Pl) + (with + (t1 t2 t3) + (and + (= t1 (= (:N (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Mi) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:N (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Lo) + (with + (t1 t2 t3) + (and + (= t1 (= (:C (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Hs) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:C (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Ne) + (with + (t1 t2 t3) + (and + (= t1 (= (:Z (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Eq) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:Z (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #x0000000000000001)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ) + ) + (require + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) +) + +(attr MInst.CSetm (tag isaspec_generated)) + +(spec + (MInst.CSetm rd cond) + (provide + (match + cond + ((Le) + (with + (t3 t4 t5) + (and + (= t3 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if t3 (= t4 #x0000000000000000) (= t5 #xffffffffffffffff)) + (= rd (if t3 t4 t5)) + ) + ) + ) + ((Gt) + (with + (t3 t4 t5) + (and + (= t3 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if t3 (= t4 #x0000000000000000) (= t5 #xffffffffffffffff)) + (= rd (if t3 t4 t5)) + ) + ) + ) + ((Lt) + (with + (t2 t3 t4) + (and + (= t2 (= (:N (:flags_in result)) (:V (:flags_in result)))) + (if t2 (= t3 #x0000000000000000) (= t4 #xffffffffffffffff)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Ge) + (with + (t2 t3 t4) + (and + (= t2 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) + (if t2 (= t3 #x0000000000000000) (= t4 #xffffffffffffffff)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Ls) + (with + (t2 t3 t4) + (and + (= t2 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if t2 (= t3 #x0000000000000000) (= t4 #xffffffffffffffff)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Hi) + (with + (t2 t3 t4) + (and + (= t2 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if t2 (= t3 #x0000000000000000) (= t4 #xffffffffffffffff)) + (= rd (if t2 t3 t4)) + ) + ) + ) + ((Vc) + (with + (t1 t2 t3) + (and + (= t1 (= (:V (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Vs) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:V (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Pl) + (with + (t1 t2 t3) + (and + (= t1 (= (:N (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Mi) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:N (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Lo) + (with + (t1 t2 t3) + (and + (= t1 (= (:C (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Hs) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:C (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Ne) + (with + (t1 t2 t3) + (and + (= t1 (= (:Z (:flags_in result)) #b1)) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ((Eq) + (with + (t1 t2 t3) + (and + (= t1 (not (= (:Z (:flags_in result)) #b1))) + (if t1 (= t2 #x0000000000000000) (= t3 #xffffffffffffffff)) + (= rd (if t1 t2 t3)) + ) + ) + ) + ) + ) + (require + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) +) + +(attr MInst.CCmp (tag isaspec_generated)) + +(spec + (MInst.CCmp size rn rm nzcv cond) + (provide + (match + size + ((Size64) + (match + cond + ((Le) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t7 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t11 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Gt) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t7 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t11 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Lt) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t6 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ge) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t6 (= (:N (:flags_in result)) (:V (:flags_in result)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ls) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t6 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Hi) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t6 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Vc) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (not (= (:V (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Vs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (= (:V (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Pl) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (not (= (:N (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Mi) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (= (:N (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Lo) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (not (= (:C (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Hs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (= (:C (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Ne) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (not (= (:Z (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Eq) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot rm)) + (= t3 (bvadd rn (bvnot rm))) + (= t5 (= (:Z (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 t1)) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ) + ) + ((Size32) + (match + cond + ((Le) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t7 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t11 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Gt) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t7 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t11 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Lt) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t6 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ge) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t6 (= (:N (:flags_in result)) (:V (:flags_in result)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ls) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t6 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Hi) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t6 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Vc) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (not (= (:V (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Vs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (= (:V (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Pl) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (not (= (:N (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Mi) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (= (:N (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Lo) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (not (= (:C (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Hs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (= (:C (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Ne) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (not (= (:Z (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Eq) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (bvnot (extract 31 0 rm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (extract 31 0 rm)))) + (= t5 (= (:Z (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 t1)) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) + ((Size32) + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) + ) + ) +) + +(attr MInst.CCmpImm (tag isaspec_generated)) + +(spec + (MInst.CCmpImm size rn imm nzcv cond) + (provide + (match + size + ((Size64) + (match + cond + ((Le) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t7 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t11 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Gt) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t7 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t11 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Lt) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t6 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ge) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t6 (= (:N (:flags_in result)) (:V (:flags_in result)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ls) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t6 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Hi) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t6 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t10 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Vc) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (not (= (:V (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Vs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (= (:V (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Pl) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (not (= (:N (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Mi) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (= (:N (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Lo) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (not (= (:C (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Hs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (= (:C (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Ne) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (not (= (:Z (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Eq) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 64 (extract 4 0 imm))) + (= t3 (bvadd rn (bvnot (zero_ext 64 (extract 4 0 imm))))) + (= t5 (= (:Z (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (sign_ext 128 rn) (sign_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 128 (bvadd t3 #x0000000000000001)) + (bvadd (bvadd (zero_ext 128 rn) (zero_ext 128 (bvnot t1))) #x00000000000000000000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x0000000000000001) #x0000000000000000) #b1 #b0)) + (= t9 (extract 63 63 (bvadd t3 #x0000000000000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ) + ) + ((Size32) + (match + cond + ((Le) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t7 (not (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0)))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t11 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Gt) + (with + (t1 t10 t11 t13 t15 t17 t19 t3 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t7 (and (= (:N (:flags_in result)) (:V (:flags_in result))) (= (:Z (:flags_in result)) #b0))) + (if + t7 + (and + (= + t8 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t9 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t10 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t11 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t13 (extract 0 0 (:V nzcv))) + (= t15 (extract 0 0 (:C nzcv))) + (= t17 (extract 0 0 (:Z nzcv))) + (= t19 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t7 t9 t15)) + (= (:N (:flags_out result)) (if t7 t11 t19)) + (= (:V (:flags_out result)) (if t7 t8 t13)) + (= (:Z (:flags_out result)) (if t7 t10 t17)) + ) + ) + ) + ((Lt) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t6 (not (= (:N (:flags_in result)) (:V (:flags_in result))))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ge) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t6 (= (:N (:flags_in result)) (:V (:flags_in result)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Ls) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t6 (not (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0)))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Hi) + (with + (t1 t10 t12 t14 t16 t18 t3 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t6 (and (= (:C (:flags_in result)) #b1) (= (:Z (:flags_in result)) #b0))) + (if + t6 + (and + (= + t7 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t8 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t9 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t10 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t12 (extract 0 0 (:V nzcv))) + (= t14 (extract 0 0 (:C nzcv))) + (= t16 (extract 0 0 (:Z nzcv))) + (= t18 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t6 t8 t14)) + (= (:N (:flags_out result)) (if t6 t10 t18)) + (= (:V (:flags_out result)) (if t6 t7 t12)) + (= (:Z (:flags_out result)) (if t6 t9 t16)) + ) + ) + ) + ((Vc) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (not (= (:V (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Vs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (= (:V (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Pl) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (not (= (:N (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Mi) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (= (:N (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Lo) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (not (= (:C (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Hs) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (= (:C (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Ne) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (not (= (:Z (:flags_in result)) #b1))) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ((Eq) + (with + (t1 t11 t13 t15 t17 t3 t5 t6 t7 t8 t9) + (and + (= t1 (zero_ext 32 (extract 4 0 imm))) + (= t3 (bvadd (extract 31 0 rn) (bvnot (zero_ext 32 (extract 4 0 imm))))) + (= t5 (= (:Z (:flags_in result)) #b1)) + (if + t5 + (and + (= + t6 + (bvnot + (if + (= + (sign_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (sign_ext 64 (extract 31 0 rn)) (sign_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= + t7 + (bvnot + (if + (= + (zero_ext 64 (bvadd t3 #x00000001)) + (bvadd (bvadd (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (bvnot t1))) #x0000000000000001) + ) + #b1 + #b0 + ) + ) + ) + (= t8 (if (= (bvadd t3 #x00000001) #x00000000) #b1 #b0)) + (= t9 (extract 31 31 (bvadd t3 #x00000001))) + ) + (and + (= t11 (extract 0 0 (:V nzcv))) + (= t13 (extract 0 0 (:C nzcv))) + (= t15 (extract 0 0 (:Z nzcv))) + (= t17 (extract 0 0 (:N nzcv))) + ) + ) + (= (:C (:flags_out result)) (if t5 t7 t13)) + (= (:N (:flags_out result)) (if t5 t9 t17)) + (= (:V (:flags_out result)) (if t5 t6 t11)) + (= (:Z (:flags_out result)) (if t5 t8 t15)) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) + ((Size32) + (match + cond + ((Le) true) + ((Gt) true) + ((Lt) true) + ((Ge) true) + ((Ls) true) + ((Hi) true) + ((Vc) true) + ((Vs) true) + ((Pl) true) + ((Mi) true) + ((Lo) true) + ((Hs) true) + ((Ne) true) + ((Eq) true) + ) + ) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/extend.isle b/cranelift/codegen/src/isa/aarch64/spec/extend.isle new file mode 100644 index 000000000000..8dab27244f83 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/extend.isle @@ -0,0 +1,49 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.Extend (tag isaspec_generated)) + +(spec + (MInst.Extend rd rn signed from_bits to_bits) + (provide + (=> (and (not signed) (= from_bits #x08) (= to_bits #x08)) (= rd (zero_ext 64 (extract 7 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x08) (= to_bits #x08)) (= rd (zero_ext 64 (sign_ext 32 (extract 7 0 (as rn (bv 64))))))) + (=> (and (not signed) (= from_bits #x08) (= to_bits #x10)) (= rd (zero_ext 64 (extract 7 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x08) (= to_bits #x10)) (= rd (zero_ext 64 (sign_ext 32 (extract 7 0 (as rn (bv 64))))))) + (=> (and (not signed) (= from_bits #x08) (= to_bits #x20)) (= rd (zero_ext 64 (extract 7 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x08) (= to_bits #x20)) (= rd (zero_ext 64 (sign_ext 32 (extract 7 0 (as rn (bv 64))))))) + (=> (and (not signed) (= from_bits #x08) (= to_bits #x40)) (= rd (zero_ext 64 (extract 7 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x08) (= to_bits #x40)) (= rd (sign_ext 64 (extract 7 0 (as rn (bv 64)))))) + (=> (and (not signed) (= from_bits #x10) (= to_bits #x10)) (= rd (zero_ext 64 (extract 15 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x10) (= to_bits #x10)) (= rd (zero_ext 64 (sign_ext 32 (extract 15 0 (as rn (bv 64))))))) + (=> (and (not signed) (= from_bits #x10) (= to_bits #x20)) (= rd (zero_ext 64 (extract 15 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x10) (= to_bits #x20)) (= rd (zero_ext 64 (sign_ext 32 (extract 15 0 (as rn (bv 64))))))) + (=> (and (not signed) (= from_bits #x10) (= to_bits #x40)) (= rd (zero_ext 64 (extract 15 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x10) (= to_bits #x40)) (= rd (sign_ext 64 (extract 15 0 (as rn (bv 64)))))) + (=> (and (not signed) (= from_bits #x20) (= to_bits #x20)) (= rd (zero_ext 64 (extract 31 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x20) (= to_bits #x20)) (= rd (zero_ext 64 (extract 31 0 (as rn (bv 64)))))) + (=> (and (not signed) (= from_bits #x20) (= to_bits #x40)) (= rd (zero_ext 64 (extract 31 0 (as rn (bv 64)))))) + (=> (and signed (= from_bits #x20) (= to_bits #x40)) (= rd (sign_ext 64 (extract 31 0 (as rn (bv 64)))))) + ) + (require + (or + (and (not signed) (= from_bits #x08) (= to_bits #x08)) + (and signed (= from_bits #x08) (= to_bits #x08)) + (and (not signed) (= from_bits #x08) (= to_bits #x10)) + (and signed (= from_bits #x08) (= to_bits #x10)) + (and (not signed) (= from_bits #x08) (= to_bits #x20)) + (and signed (= from_bits #x08) (= to_bits #x20)) + (and (not signed) (= from_bits #x08) (= to_bits #x40)) + (and signed (= from_bits #x08) (= to_bits #x40)) + (and (not signed) (= from_bits #x10) (= to_bits #x10)) + (and signed (= from_bits #x10) (= to_bits #x10)) + (and (not signed) (= from_bits #x10) (= to_bits #x20)) + (and signed (= from_bits #x10) (= to_bits #x20)) + (and (not signed) (= from_bits #x10) (= to_bits #x40)) + (and signed (= from_bits #x10) (= to_bits #x40)) + (and (not signed) (= from_bits #x20) (= to_bits #x20)) + (and signed (= from_bits #x20) (= to_bits #x20)) + (and (not signed) (= from_bits #x20) (= to_bits #x40)) + (and signed (= from_bits #x20) (= to_bits #x40)) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fp_const.isle b/cranelift/codegen/src/isa/aarch64/spec/fp_const.isle new file mode 100644 index 000000000000..d98c1b80ea9c --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fp_const.isle @@ -0,0 +1,79 @@ +;; GENERATED BY `fpconst`. DO NOT EDIT!!! + +(spec (min_fp_value signed in out) + (provide + (=> (and (= in #x20) (= signed true) (= out #x08)) (= result #x00000000c3010000)) + (=> (and (= in #x20) (= signed true) (= out #x10)) (= result #x00000000c7000100)) + (=> (and (= in #x20) (= signed true) (= out #x20)) (= result #x00000000cf000000)) + (=> (and (= in #x20) (= signed true) (= out #x40)) (= result #x00000000df000000)) + (=> (and (= in #x20) (= signed false) (= out #x08)) (= result #x00000000bf800000)) + (=> (and (= in #x20) (= signed false) (= out #x10)) (= result #x00000000bf800000)) + (=> (and (= in #x20) (= signed false) (= out #x20)) (= result #x00000000bf800000)) + (=> (and (= in #x20) (= signed false) (= out #x40)) (= result #x00000000bf800000)) + (=> (and (= in #x40) (= signed true) (= out #x08)) (= result #xc060200000000000)) + (=> (and (= in #x40) (= signed true) (= out #x10)) (= result #xc0e0002000000000)) + (=> (and (= in #x40) (= signed true) (= out #x20)) (= result #xc1e0000000200000)) + (=> (and (= in #x40) (= signed true) (= out #x40)) (= result #xc3e0000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x08)) (= result #xbff0000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x10)) (= result #xbff0000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x20)) (= result #xbff0000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x40)) (= result #xbff0000000000000)) + ) + (require (or + (and (= in #x20) (= signed true) (= out #x08)) + (and (= in #x20) (= signed true) (= out #x10)) + (and (= in #x20) (= signed true) (= out #x20)) + (and (= in #x20) (= signed true) (= out #x40)) + (and (= in #x20) (= signed false) (= out #x08)) + (and (= in #x20) (= signed false) (= out #x10)) + (and (= in #x20) (= signed false) (= out #x20)) + (and (= in #x20) (= signed false) (= out #x40)) + (and (= in #x40) (= signed true) (= out #x08)) + (and (= in #x40) (= signed true) (= out #x10)) + (and (= in #x40) (= signed true) (= out #x20)) + (and (= in #x40) (= signed true) (= out #x40)) + (and (= in #x40) (= signed false) (= out #x08)) + (and (= in #x40) (= signed false) (= out #x10)) + (and (= in #x40) (= signed false) (= out #x20)) + (and (= in #x40) (= signed false) (= out #x40)) + )) +) + +(spec (max_fp_value signed in out) + (provide + (=> (and (= in #x20) (= signed true) (= out #x08)) (= result #x0000000043000000)) + (=> (and (= in #x20) (= signed true) (= out #x10)) (= result #x0000000047000000)) + (=> (and (= in #x20) (= signed true) (= out #x20)) (= result #x000000004f000000)) + (=> (and (= in #x20) (= signed true) (= out #x40)) (= result #x000000005f000000)) + (=> (and (= in #x20) (= signed false) (= out #x08)) (= result #x0000000043800000)) + (=> (and (= in #x20) (= signed false) (= out #x10)) (= result #x0000000047800000)) + (=> (and (= in #x20) (= signed false) (= out #x20)) (= result #x000000004f800000)) + (=> (and (= in #x20) (= signed false) (= out #x40)) (= result #x000000005f800000)) + (=> (and (= in #x40) (= signed true) (= out #x08)) (= result #x4060000000000000)) + (=> (and (= in #x40) (= signed true) (= out #x10)) (= result #x40e0000000000000)) + (=> (and (= in #x40) (= signed true) (= out #x20)) (= result #x41e0000000000000)) + (=> (and (= in #x40) (= signed true) (= out #x40)) (= result #x43e0000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x08)) (= result #x4070000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x10)) (= result #x40f0000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x20)) (= result #x41f0000000000000)) + (=> (and (= in #x40) (= signed false) (= out #x40)) (= result #x43f0000000000000)) + ) + (require (or + (and (= in #x20) (= signed true) (= out #x08)) + (and (= in #x20) (= signed true) (= out #x10)) + (and (= in #x20) (= signed true) (= out #x20)) + (and (= in #x20) (= signed true) (= out #x40)) + (and (= in #x20) (= signed false) (= out #x08)) + (and (= in #x20) (= signed false) (= out #x10)) + (and (= in #x20) (= signed false) (= out #x20)) + (and (= in #x20) (= signed false) (= out #x40)) + (and (= in #x40) (= signed true) (= out #x08)) + (and (= in #x40) (= signed true) (= out #x10)) + (and (= in #x40) (= signed true) (= out #x20)) + (and (= in #x40) (= signed true) (= out #x40)) + (and (= in #x40) (= signed false) (= out #x08)) + (and (= in #x40) (= signed false) (= out #x10)) + (and (= in #x40) (= signed false) (= out #x20)) + (and (= in #x40) (= signed false) (= out #x40)) + )) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_cmp.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_cmp.isle new file mode 100644 index 000000000000..b920160767ad --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_cmp.isle @@ -0,0 +1,37 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuCmp (tag isaspec_generated)) + +(spec + (MInst.FpuCmp size rn rm) + (provide + (match + size + ((Size64) + (with + (t4) + (and + (= t4 (FPCompare! (extract 63 0 (conv_to 128 (as rn (bv 64)))) (extract 63 0 (conv_to 128 (as rm (bv 64)))) false fpcr)) + (= (:V (:flags_out result)) (extract 0 0 t4)) + (= (:C (:flags_out result)) (extract 1 1 t4)) + (= (:Z (:flags_out result)) (extract 2 2 t4)) + (= (:N (:flags_out result)) (extract 3 3 t4)) + ) + ) + ) + ((Size32) + (with + (t4) + (and + (= t4 (FPCompare! (extract 31 0 (conv_to 128 (as rn (bv 64)))) (extract 31 0 (conv_to 128 (as rm (bv 64)))) false fpcr)) + (= (:V (:flags_out result)) (extract 0 0 t4)) + (= (:C (:flags_out result)) (extract 1 1 t4)) + (= (:Z (:flags_out result)) (extract 2 2 t4)) + (= (:N (:flags_out result)) (extract 3 3 t4)) + ) + ) + ) + ) + ) + (require (match size ((Size64) true) ((Size32) true))) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_move_imm.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_move_imm.isle new file mode 100644 index 000000000000..4e04884cbb71 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_move_imm.isle @@ -0,0 +1,43 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuMoveFPImm (tag isaspec_generated)) + +(spec + (MInst.FpuMoveFPImm rd imm size) + (provide + (match + size + ((Size64) + (= + rd + (zero_ext + 128 + (concat + (concat + (extract 7 7 (:imm imm)) + (concat (concat (bvnot (extract 6 6 (:imm imm))) (replicate (extract 6 6 (:imm imm)) 8)) (extract 5 4 (:imm imm))) + ) + (concat (extract 3 0 (:imm imm)) #x000000000000) + ) + ) + ) + ) + ((Size32) + (= + rd + (zero_ext + 128 + (concat + (concat + (extract 7 7 (:imm imm)) + (concat (concat (bvnot (extract 6 6 (:imm imm))) (replicate (extract 6 6 (:imm imm)) 5)) (extract 5 4 (:imm imm))) + ) + (concat (extract 3 0 (:imm imm)) #b0000000000000000000) + ) + ) + ) + ) + ) + ) + (require (match size ((Size64) true) ((Size32) true))) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_round.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_round.isle new file mode 100644 index 000000000000..da4c76835985 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_round.isle @@ -0,0 +1,97 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuRound (tag isaspec_generated)) + +(spec + (MInst.FpuRound op rd rn) + (provide + (match + op + ((Nearest64) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 63 0 (conv_to 128 (as rn (bv 64)))) fpcr 0 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Nearest32) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 31 0 (conv_to 128 (as rn (bv 64)))) fpcr 0 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Zero64) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 63 0 (conv_to 128 (as rn (bv 64)))) fpcr 3 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Zero32) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 31 0 (conv_to 128 (as rn (bv 64)))) fpcr 3 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Plus64) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 63 0 (conv_to 128 (as rn (bv 64)))) fpcr 1 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Plus32) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 31 0 (conv_to 128 (as rn (bv 64)))) fpcr 1 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Minus64) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 63 0 (conv_to 128 (as rn (bv 64)))) fpcr 2 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Minus32) + (with + (t3) + (and + (= t3 (FPRoundInt! (extract 31 0 (conv_to 128 (as rn (bv 64)))) fpcr 2 false)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ) + ) + (require + (match + op + ((Nearest64) true) + ((Nearest32) true) + ((Zero64) true) + ((Zero32) true) + ((Plus64) true) + ((Plus32) true) + ((Minus64) true) + ((Minus32) true) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_rr.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_rr.isle new file mode 100644 index 000000000000..c49bd0fd4750 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_rr.isle @@ -0,0 +1,87 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuRR (tag isaspec_generated)) + +(spec + (MInst.FpuRR fpu_op size rd rn) + (provide + (match + size + ((Size64) + (match + fpu_op + ((Neg) + (= + (conv_to 128 (as rd (bv 64))) + (zero_ext + 128 + (concat (bvnot (extract 63 63 (conv_to 128 (as rn (bv 64))))) (extract 62 0 (conv_to 128 (as rn (bv 64))))) + ) + ) + ) + ((Abs) (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (extract 62 0 (conv_to 128 (as rn (bv 64))))))) + ((Sqrt) + (with + (t2) + (and + (= t2 (FPSqrt! (extract 63 0 (conv_to 128 (as rn (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t2)) + ) + ) + ) + ((Cvt64To32) + (with + (t1 t3) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t3 (FPConvert! (extract 63 0 (conv_to 128 (as rn (bv 64)))) fpcr (bv2nat t1) 32 64)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ) + ) + ((Size32) + (match + fpu_op + ((Neg) + (= + (conv_to 128 (as rd (bv 64))) + (zero_ext + 128 + (concat (bvnot (extract 31 31 (conv_to 128 (as rn (bv 64))))) (extract 30 0 (conv_to 128 (as rn (bv 64))))) + ) + ) + ) + ((Abs) (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (extract 30 0 (conv_to 128 (as rn (bv 64))))))) + ((Sqrt) + (with + (t2) + (and + (= t2 (FPSqrt! (extract 31 0 (conv_to 128 (as rn (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t2)) + ) + ) + ) + ((Cvt32To64) + (with + (t1 t3) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t3 (FPConvert! (extract 31 0 (conv_to 128 (as rn (bv 64)))) fpcr (bv2nat t1) 64 32)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) (match fpu_op ((Neg) true) ((Abs) true) ((Sqrt) true) ((Cvt64To32) true))) + ((Size32) (match fpu_op ((Neg) true) ((Abs) true) ((Sqrt) true) ((Cvt32To64) true))) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_rri.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_rri.isle new file mode 100644 index 000000000000..81589f7cce08 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_rri.isle @@ -0,0 +1,30 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuRRI (tag isaspec_generated)) + +(spec + (MInst.FpuRRI fpu_op rd rn) + (provide + (=> + (= (:lane_size_in_bits fpu_op) #x40) + (= + (conv_to 128 (as rd (bv 64))) + (zero_ext 128 (extract 63 0 (bvashr (zero_ext 128 (extract 63 0 (conv_to 128 (as rn (bv 64))))) (zero_ext 128 #x003f)))) + ) + ) + (=> + (= (:lane_size_in_bits fpu_op) #x20) + (= + (conv_to 128 (as rd (bv 64))) + (zero_ext + 128 + (concat + (extract 31 0 (bvashr (zero_ext 64 (extract 63 32 (conv_to 128 (as rn (bv 64))))) (zero_ext 64 #x001f))) + (extract 31 0 (bvashr (zero_ext 64 (extract 31 0 (conv_to 128 (as rn (bv 64))))) (zero_ext 64 #x001f))) + ) + ) + ) + ) + ) + (require (or (= (:lane_size_in_bits fpu_op) #x40) (= (:lane_size_in_bits fpu_op) #x20))) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_rrimod.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_rrimod.isle new file mode 100644 index 000000000000..32a0e3af8d7d --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_rrimod.isle @@ -0,0 +1,30 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuRRIMod (tag isaspec_generated)) + +(spec + (MInst.FpuRRIMod fpu_op rd ri rn) + (provide + (=> + (= (:lane_size_in_bits fpu_op) #x40) + (= + (conv_to 128 (as rd (bv 64))) + (zero_ext 128 (concat (extract 0 0 (conv_to 128 (as rn (bv 64)))) (extract 62 0 (conv_to 128 (as ri (bv 64)))))) + ) + ) + (=> + (= (:lane_size_in_bits fpu_op) #x20) + (= + (conv_to 128 (as rd (bv 64))) + (zero_ext + 128 + (concat + (concat (extract 32 32 (conv_to 128 (as rn (bv 64)))) (extract 62 32 (conv_to 128 (as ri (bv 64))))) + (concat (extract 0 0 (conv_to 128 (as rn (bv 64)))) (extract 30 0 (conv_to 128 (as ri (bv 64))))) + ) + ) + ) + ) + ) + (require (or (= (:lane_size_in_bits fpu_op) #x40) (= (:lane_size_in_bits fpu_op) #x20))) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_rrr.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_rrr.isle new file mode 100644 index 000000000000..f86e10cde7c5 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_rrr.isle @@ -0,0 +1,137 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuRRR (tag isaspec_generated)) + +(spec + (MInst.FpuRRR fpu_op size rd rn rm) + (provide + (match + size + ((Size64) + (match + fpu_op + ((Add) + (with + (t3) + (and + (= t3 (FPAdd! (extract 63 0 (conv_to 128 (as rn (bv 64)))) (extract 63 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Sub) + (with + (t3) + (and + (= t3 (FPSub! (extract 63 0 (conv_to 128 (as rn (bv 64)))) (extract 63 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Mul) + (with + (t3) + (and + (= t3 (FPMul! (extract 63 0 (conv_to 128 (as rn (bv 64)))) (extract 63 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Div) + (with + (t3) + (and + (= t3 (FPDiv! (extract 63 0 (conv_to 128 (as rn (bv 64)))) (extract 63 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Min) + (with + (t3) + (and + (= t3 (FPMin! (extract 63 0 (conv_to 128 (as rn (bv 64)))) (extract 63 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Max) + (with + (t3) + (and + (= t3 (FPMax! (extract 63 0 (conv_to 128 (as rn (bv 64)))) (extract 63 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ) + ) + ((Size32) + (match + fpu_op + ((Add) + (with + (t3) + (and + (= t3 (FPAdd! (extract 31 0 (conv_to 128 (as rn (bv 64)))) (extract 31 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Sub) + (with + (t3) + (and + (= t3 (FPSub! (extract 31 0 (conv_to 128 (as rn (bv 64)))) (extract 31 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Mul) + (with + (t3) + (and + (= t3 (FPMul! (extract 31 0 (conv_to 128 (as rn (bv 64)))) (extract 31 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Div) + (with + (t3) + (and + (= t3 (FPDiv! (extract 31 0 (conv_to 128 (as rn (bv 64)))) (extract 31 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Min) + (with + (t3) + (and + (= t3 (FPMin! (extract 31 0 (conv_to 128 (as rn (bv 64)))) (extract 31 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ((Max) + (with + (t3) + (and + (= t3 (FPMax! (extract 31 0 (conv_to 128 (as rn (bv 64)))) (extract 31 0 (conv_to 128 (as rm (bv 64)))) fpcr)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 t3)) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) (match fpu_op ((Add) true) ((Sub) true) ((Mul) true) ((Div) true) ((Min) true) ((Max) true))) + ((Size32) (match fpu_op ((Add) true) ((Sub) true) ((Mul) true) ((Div) true) ((Min) true) ((Max) true))) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/fpu_to_int.isle b/cranelift/codegen/src/isa/aarch64/spec/fpu_to_int.isle new file mode 100644 index 000000000000..5faa156aabef --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/fpu_to_int.isle @@ -0,0 +1,33 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.FpuToInt (tag isaspec_generated)) + +(spec + (MInst.FpuToInt op rd rn) + (provide + (match + op + ((F64ToI64) (with (t3) (and (= t3 (FPToFixed! (extract 63 0 rn) 0 false fpcr 3 64 64)) (= rd t3)))) + ((F64ToU64) (with (t3) (and (= t3 (FPToFixed! (extract 63 0 rn) 0 true fpcr 3 64 64)) (= rd t3)))) + ((F64ToI32) (with (t3) (and (= t3 (FPToFixed! (extract 63 0 rn) 0 false fpcr 3 32 64)) (= rd (zero_ext 64 t3))))) + ((F64ToU32) (with (t3) (and (= t3 (FPToFixed! (extract 63 0 rn) 0 true fpcr 3 32 64)) (= rd (zero_ext 64 t3))))) + ((F32ToI64) (with (t3) (and (= t3 (FPToFixed! (extract 31 0 rn) 0 false fpcr 3 64 32)) (= rd t3)))) + ((F32ToU64) (with (t3) (and (= t3 (FPToFixed! (extract 31 0 rn) 0 true fpcr 3 64 32)) (= rd t3)))) + ((F32ToI32) (with (t3) (and (= t3 (FPToFixed! (extract 31 0 rn) 0 false fpcr 3 32 32)) (= rd (zero_ext 64 t3))))) + ((F32ToU32) (with (t3) (and (= t3 (FPToFixed! (extract 31 0 rn) 0 true fpcr 3 32 32)) (= rd (zero_ext 64 t3))))) + ) + ) + (require + (match + op + ((F64ToI64) true) + ((F64ToU64) true) + ((F64ToI32) true) + ((F64ToU32) true) + ((F32ToI64) true) + ((F32ToU64) true) + ((F32ToI32) true) + ((F32ToU32) true) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/int_to_fpu.isle b/cranelift/codegen/src/isa/aarch64/spec/int_to_fpu.isle new file mode 100644 index 000000000000..00a1ce36d968 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/int_to_fpu.isle @@ -0,0 +1,105 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.IntToFpu (tag isaspec_generated)) + +(spec + (MInst.IntToFpu op rd rn) + (provide + (match + op + ((I64ToF64) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! rn 0 false fpcr (bv2nat t1) 64 64)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ((U64ToF64) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! rn 0 true fpcr (bv2nat t1) 64 64)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ((I64ToF32) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! rn 0 false fpcr (bv2nat t1) 64 32)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ((U64ToF32) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! rn 0 true fpcr (bv2nat t1) 64 32)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ((I32ToF64) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! (extract 31 0 rn) 0 false fpcr (bv2nat t1) 32 64)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ((U32ToF64) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! (extract 31 0 rn) 0 true fpcr (bv2nat t1) 32 64)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ((I32ToF32) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! (extract 31 0 rn) 0 false fpcr (bv2nat t1) 32 32)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ((U32ToF32) + (with + (t1 t4) + (and + (= t1 (zero_ext 4 (extract 23 22 fpcr))) + (= t4 (FixedToFP! (extract 31 0 rn) 0 true fpcr (bv2nat t1) 32 32)) + (= rd (zero_ext 128 t4)) + ) + ) + ) + ) + ) + (require + (match + op + ((I64ToF64) true) + ((U64ToF64) true) + ((I64ToF32) true) + ((U64ToF32) true) + ((I32ToF64) true) + ((U32ToF64) true) + ((I32ToF32) true) + ((U32ToF32) true) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/loads.isle b/cranelift/codegen/src/isa/aarch64/spec/loads.isle new file mode 100644 index 000000000000..2360bf7f654f --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/loads.isle @@ -0,0 +1,1009 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.ULoad8 (tag isaspec_generated)) + +(spec + (MInst.ULoad8 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 11 0 uimm12)))) + (= rd (zero_ext 64 (conv_to 8 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.SLoad8 (tag isaspec_generated)) + +(spec + (MInst.SLoad8 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 8) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 11 0 uimm12)))) + (= rd (sign_ext 64 (conv_to 8 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.ULoad16 (tag isaspec_generated)) + +(spec + (MInst.ULoad16 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (concat (extract 62 0 rm) #b0))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b0)))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b0)))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 62 0 rm) #b0)))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (concat (zero_ext 63 (extract 11 0 uimm12)) #b0))) + (= rd (zero_ext 64 (conv_to 16 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.SLoad16 (tag isaspec_generated)) + +(spec + (MInst.SLoad16 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (concat (extract 62 0 rm) #b0))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b0)))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b0)))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 62 0 rm) #b0)))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 16) + (= (:addr isa_load) (bvadd rn (concat (zero_ext 63 (extract 11 0 uimm12)) #b0))) + (= rd (sign_ext 64 (conv_to 16 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.ULoad32 (tag isaspec_generated)) + +(spec + (MInst.ULoad32 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (concat (extract 61 0 rm) #b00))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b00)))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b00)))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 61 0 rm) #b00)))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (concat (zero_ext 62 (extract 11 0 uimm12)) #b00))) + (= rd (zero_ext 64 (conv_to 32 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.SLoad32 (tag isaspec_generated)) + +(spec + (MInst.SLoad32 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (concat (extract 61 0 rm) #b00))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b00)))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b00)))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 61 0 rm) #b00)))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (concat (zero_ext 62 (extract 11 0 uimm12)) #b00))) + (= rd (sign_ext 64 (conv_to 32 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.ULoad64 (tag isaspec_generated)) + +(spec + (MInst.ULoad64 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn rm)) + (= rd (conv_to 64 loaded_value)) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (concat (extract 60 0 rm) #b000))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b000)))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b000)))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 60 0 rm) #b000)))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (concat (zero_ext 61 (extract 11 0 uimm12)) #b000))) + (= rd (conv_to 64 loaded_value)) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.FpuLoad32 (tag isaspec_generated)) + +(spec + (MInst.FpuLoad32 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn rm)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (concat (extract 61 0 rm) #b00))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b00)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b00)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 61 0 rm) #b00)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 32) + (= (:addr isa_load) (bvadd rn (concat (zero_ext 62 (extract 11 0 uimm12)) #b00))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 32 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.FpuLoad64 (tag isaspec_generated)) + +(spec + (MInst.FpuLoad64 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn rm)) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (concat (extract 60 0 rm) #b000))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b000)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b000)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (concat (extract 60 0 rm) #b000)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ((SXTW) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ((SXTX) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_load) true) + (= (:size_bits isa_load) 64) + (= (:addr isa_load) (bvadd rn (concat (zero_ext 61 (extract 11 0 uimm12)) #b000))) + (= (conv_to 128 (as rd (bv 64))) (zero_ext 128 (conv_to 64 loaded_value))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/mov_from_vec.isle b/cranelift/codegen/src/isa/aarch64/spec/mov_from_vec.isle new file mode 100644 index 000000000000..51989dc5212a --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/mov_from_vec.isle @@ -0,0 +1,86 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.MovFromVec (tag isaspec_generated)) + +(spec + (MInst.MovFromVec rd rn idx size) + (provide + (match + size + ((Size64) + (and + (=> (= idx #x00) (= rd (extract 63 0 (as rn (bv 128))))) + (=> (= idx #x01) (= rd (extract 127 64 (as rn (bv 128))))) + ) + ) + ((Size32) + (and + (=> (= idx #x00) (= rd (zero_ext 64 (extract 31 0 (as rn (bv 128)))))) + (=> (= idx #x01) (= rd (zero_ext 64 (extract 63 32 (as rn (bv 128)))))) + (=> (= idx #x02) (= rd (zero_ext 64 (extract 95 64 (as rn (bv 128)))))) + (=> (= idx #x03) (= rd (zero_ext 64 (extract 127 96 (as rn (bv 128)))))) + ) + ) + ((Size16) + (and + (=> (= idx #x00) (= rd (zero_ext 64 (extract 15 0 (as rn (bv 128)))))) + (=> (= idx #x01) (= rd (zero_ext 64 (extract 31 16 (as rn (bv 128)))))) + (=> (= idx #x02) (= rd (zero_ext 64 (extract 47 32 (as rn (bv 128)))))) + (=> (= idx #x03) (= rd (zero_ext 64 (extract 63 48 (as rn (bv 128)))))) + (=> (= idx #x04) (= rd (zero_ext 64 (extract 79 64 (as rn (bv 128)))))) + (=> (= idx #x05) (= rd (zero_ext 64 (extract 95 80 (as rn (bv 128)))))) + (=> (= idx #x06) (= rd (zero_ext 64 (extract 111 96 (as rn (bv 128)))))) + (=> (= idx #x07) (= rd (zero_ext 64 (extract 127 112 (as rn (bv 128)))))) + ) + ) + ((Size8) + (and + (=> (= idx #x00) (= rd (zero_ext 64 (extract 7 0 (as rn (bv 128)))))) + (=> (= idx #x01) (= rd (zero_ext 64 (extract 15 8 (as rn (bv 128)))))) + (=> (= idx #x02) (= rd (zero_ext 64 (extract 23 16 (as rn (bv 128)))))) + (=> (= idx #x03) (= rd (zero_ext 64 (extract 31 24 (as rn (bv 128)))))) + (=> (= idx #x04) (= rd (zero_ext 64 (extract 39 32 (as rn (bv 128)))))) + (=> (= idx #x05) (= rd (zero_ext 64 (extract 47 40 (as rn (bv 128)))))) + (=> (= idx #x06) (= rd (zero_ext 64 (extract 55 48 (as rn (bv 128)))))) + (=> (= idx #x07) (= rd (zero_ext 64 (extract 63 56 (as rn (bv 128)))))) + (=> (= idx #x08) (= rd (zero_ext 64 (extract 71 64 (as rn (bv 128)))))) + (=> (= idx #x09) (= rd (zero_ext 64 (extract 79 72 (as rn (bv 128)))))) + (=> (= idx #x0a) (= rd (zero_ext 64 (extract 87 80 (as rn (bv 128)))))) + (=> (= idx #x0b) (= rd (zero_ext 64 (extract 95 88 (as rn (bv 128)))))) + (=> (= idx #x0c) (= rd (zero_ext 64 (extract 103 96 (as rn (bv 128)))))) + (=> (= idx #x0d) (= rd (zero_ext 64 (extract 111 104 (as rn (bv 128)))))) + (=> (= idx #x0e) (= rd (zero_ext 64 (extract 119 112 (as rn (bv 128)))))) + (=> (= idx #x0f) (= rd (zero_ext 64 (extract 127 120 (as rn (bv 128)))))) + ) + ) + ) + ) + (require + (match + size + ((Size64) (or (= idx #x00) (= idx #x01))) + ((Size32) (or (= idx #x00) (= idx #x01) (= idx #x02) (= idx #x03))) + ((Size16) (or (= idx #x00) (= idx #x01) (= idx #x02) (= idx #x03) (= idx #x04) (= idx #x05) (= idx #x06) (= idx #x07))) + ((Size8) + (or + (= idx #x00) + (= idx #x01) + (= idx #x02) + (= idx #x03) + (= idx #x04) + (= idx #x05) + (= idx #x06) + (= idx #x07) + (= idx #x08) + (= idx #x09) + (= idx #x0a) + (= idx #x0b) + (= idx #x0c) + (= idx #x0d) + (= idx #x0e) + (= idx #x0f) + ) + ) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/mov_to_fpu.isle b/cranelift/codegen/src/isa/aarch64/spec/mov_to_fpu.isle new file mode 100644 index 000000000000..84ac2e623378 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/mov_to_fpu.isle @@ -0,0 +1,16 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.MovToFpu (tag isaspec_generated)) + +(spec + (MInst.MovToFpu rd rn size) + (provide + (match + size + ((Size64) (= rd (zero_ext 128 (as rn (bv 64))))) + ((Size32) (= rd (zero_ext 128 (extract 31 0 (as rn (bv 64)))))) + ((Size16) (= rd (zero_ext 128 (extract 15 0 (as rn (bv 64)))))) + ) + ) + (require (match size ((Size64) true) ((Size32) true) ((Size16) true))) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/mov_wide.isle b/cranelift/codegen/src/isa/aarch64/spec/mov_wide.isle new file mode 100644 index 000000000000..d0c99b57d153 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/mov_wide.isle @@ -0,0 +1,69 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.MovWide (tag isaspec_generated)) + +(spec + (MInst.MovWide op rd imm size) + (provide + (match + size + ((Size64) + (match + op + ((MovZ) + (and + (=> (= (:shift imm) #b00) (= rd (zero_ext 64 (extract 15 0 (:bits imm))))) + (=> (= (:shift imm) #b01) (= rd (zero_ext 64 (concat (extract 15 0 (:bits imm)) #x0000)))) + (=> (= (:shift imm) #b10) (= rd (zero_ext 64 (concat (extract 15 0 (:bits imm)) #x00000000)))) + (=> (= (:shift imm) #b11) (= rd (concat (extract 15 0 (:bits imm)) #x000000000000))) + ) + ) + ((MovN) + (and + (=> (= (:shift imm) #b00) (= rd (bvnot (zero_ext 64 (extract 15 0 (:bits imm)))))) + (=> (= (:shift imm) #b01) (= rd (bvnot (zero_ext 64 (concat (extract 15 0 (:bits imm)) #x0000))))) + (=> (= (:shift imm) #b10) (= rd (bvnot (zero_ext 64 (concat (extract 15 0 (:bits imm)) #x00000000))))) + (=> (= (:shift imm) #b11) (= rd (bvnot (concat (extract 15 0 (:bits imm)) #x000000000000)))) + ) + ) + ) + ) + ((Size32) + (match + op + ((MovZ) + (and + (=> (= (:shift imm) #b00) (= rd (zero_ext 64 (extract 15 0 (:bits imm))))) + (=> (= (:shift imm) #b01) (= rd (zero_ext 64 (concat (extract 15 0 (:bits imm)) #x0000)))) + ) + ) + ((MovN) + (and + (=> (= (:shift imm) #b00) (= rd (zero_ext 64 (bvnot (zero_ext 32 (extract 15 0 (:bits imm))))))) + (=> (= (:shift imm) #b01) (= rd (zero_ext 64 (bvnot (concat (extract 15 0 (:bits imm)) #x0000))))) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size64) + (match + op + ((MovZ) (or (= (:shift imm) #b00) (= (:shift imm) #b01) (= (:shift imm) #b10) (= (:shift imm) #b11))) + ((MovN) (or (= (:shift imm) #b00) (= (:shift imm) #b01) (= (:shift imm) #b10) (= (:shift imm) #b11))) + ) + ) + ((Size32) + (match + op + ((MovZ) (or (= (:shift imm) #b00) (= (:shift imm) #b01))) + ((MovN) (or (= (:shift imm) #b00) (= (:shift imm) #b01))) + ) + ) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/primitives.isle b/cranelift/codegen/src/isa/aarch64/spec/primitives.isle new file mode 100644 index 000000000000..2d32cc4df5ea --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/primitives.isle @@ -0,0 +1,860 @@ +;; ASLp primitive function definitions. + +; Floating point primitives for AArch64 adapted from "Arm A-profile A64 +; Instruction Set Architecture" shared pseudocode definitions. +; +; See: https://developer.arm.com/documentation/ddi0602/2024-09/Shared-Pseudocode/shared-functions-float + +; // FPDefaultNaN() +; // ============== +; +; bits(N) FPDefaultNaN(FPCR_Type fpcr, integer N) +; assert N IN {16,32,64}; +; constant integer E = (if N == 16 then 5 elsif N == 32 then 8 else 11); +; constant integer F = N - (E + 1); +; constant bit sign = if IsFeatureImplemented(FEAT_AFP) && !UsingAArch32() then fpcr.AH else '0'; +; +; constant bits(E) exp = Ones(E); +; constant bits(F) frac = '1':Zeros(F-1); +; return sign : exp : frac; +(macro (FPDefaultNaN w) + (conv_to w + (switch w + (32 #x000000007fc00000) + (64 #x7ff8000000000000) + ) + ) +) + +; // FPProcessNaN() +; // ============== +; // Handle NaN input operands, returning the operand or default NaN value +; // if fpcr.DN is selected. The 'fpcr' argument supplies the FPCR control bits. +; // The 'fpexc' argument controls the generation of exceptions, regardless of +; // whether 'fptype' is a signalling NaN or a quiet NaN. +; +; bits(N) FPProcessNaN(FPType fptype, bits(N) op, FPCR_Type fpcr, boolean fpexc) +; assert N IN {16,32,64}; +; assert fptype IN {FPType_QNaN, FPType_SNaN}; +; integer topfrac; +; +; case N of +; when 16 topfrac = 9; +; when 32 topfrac = 22; +; when 64 topfrac = 51; +; +; result = op; +; if fptype == FPType_SNaN then +; result = '1'; +; if fpexc then FPProcessException(FPExc_InvalidOp, fpcr); +; if fpcr.DN == '1' then // DefaultNaN requested +; result = FPDefaultNaN(fpcr, N); +; return result; +(macro (FPProcessNaN x) (bvor x (fp_topfrac_bit_set! (widthof x)))) + +; // FPProcessNaNs() +; // =============== +; // +; // The boolean part of the return value says whether a NaN has been found and +; // processed. The bits(N) part is only relevant if it has and supplies the +; // result of the operation. +; // +; // The 'fpcr' argument supplies FPCR control bits and 'altfmaxfmin' controls +; // alternative floating-point behavior for FMAX, FMIN and variants. 'fpexc' +; // controls the generation of floating-point exceptions. Status information +; // is updated directly in the FPSR where appropriate. +; +; (boolean, bits(N)) FPProcessNaNs(FPType type1, FPType type2, bits(N) op1, bits(N) op2, +; FPCR_Type fpcr, boolean fpexc) +; assert N IN {16,32,64}; +; boolean done; +; bits(N) result; +; constant boolean altfp = IsFeatureImplemented(FEAT_AFP) && !UsingAArch32() && fpcr.AH == '1'; +; constant boolean op1_nan = type1 IN {FPType_SNaN, FPType_QNaN}; +; constant boolean op2_nan = type2 IN {FPType_SNaN, FPType_QNaN}; +; constant boolean any_snan = type1 == FPType_SNaN || type2 == FPType_SNaN; +; constant FPType type_nan = if any_snan then FPType_SNaN else FPType_QNaN; +; +; if altfp && op1_nan && op2_nan then +; // register NaN selected +; done = TRUE; result = FPProcessNaN(type_nan, op1, fpcr, fpexc); +; elsif type1 == FPType_SNaN then +; done = TRUE; result = FPProcessNaN(type1, op1, fpcr, fpexc); +; elsif type2 == FPType_SNaN then +; done = TRUE; result = FPProcessNaN(type2, op2, fpcr, fpexc); +; elsif type1 == FPType_QNaN then +; done = TRUE; result = FPProcessNaN(type1, op1, fpcr, fpexc); +; elsif type2 == FPType_QNaN then +; done = TRUE; result = FPProcessNaN(type2, op2, fpcr, fpexc); +; else +; done = FALSE; result = Zeros(N); // 'Don't care' result +; return (done, result); +; +; Note: spec included for reference, details are inlined in FPAdd. + +;; FPAdd: Floating point addition +(macro (FPAdd x y fpcr) + ; // FPAdd() + ; // ======= + ; + ; bits(N) FPAdd(bits(N) op1, bits(N) op2, FPCR_Type fpcr, boolean fpexc) + ; + ; assert N IN {16,32,64}; + ; rounding = FPRoundingMode(fpcr); + ; + ; (type1,sign1,value1) = FPUnpack(op1, fpcr, fpexc); + ; (type2,sign2,value2) = FPUnpack(op2, fpcr, fpexc); + (let + ( + (sign1 (fp.isNegative x)) + (sign2 (fp.isNegative y)) + ) + ; + ; (done,result) = FPProcessNaNs(type1, type2, op1, op2, fpcr, fpexc); + (if (fp.isNaN x) + (FPProcessNaN! x) + (if (fp.isNaN y) + (FPProcessNaN! y) + ; if !done then + ; inf1 = (type1 == FPType_Infinity); inf2 = (type2 == FPType_Infinity); + ; zero1 = (type1 == FPType_Zero); zero2 = (type2 == FPType_Zero); + (let + ( + (inf1 (fp.isInfinite x)) + (inf2 (fp.isInfinite y)) + (zero1 (fp.isZero x)) + (zero2 (fp.isZero y)) + ) + ; if inf1 && inf2 && sign1 == NOT(sign2) then + ; result = FPDefaultNaN(fpcr, N); + ; if fpexc then FPProcessException(FPExc_InvalidOp, fpcr); + (if (and inf1 inf2 (= sign1 (not sign2))) + (FPDefaultNaN! (widthof x)) + ; elsif (inf1 && sign1 == '0') || (inf2 && sign2 == '0') then + ; result = FPInfinity('0', N); + (if (or (and inf1 (not sign1)) (and inf2 (not sign2))) + (fp.+oo (widthof x)) + ; elsif (inf1 && sign1 == '1') || (inf2 && sign2 == '1') then + ; result = FPInfinity('1', N); + (if (or (and inf1 sign1) (and inf2 sign2)) + (fp.-oo (widthof x)) + ; elsif zero1 && zero2 && sign1 == sign2 then + ; result = FPZero(sign1, N); + (if (and zero1 zero2 (= sign1 sign2)) + (fp_signed_zero! sign1 (widthof x)) + ; else + ; result_value = value1 + value2; + ; if result_value == 0.0 then // Sign of exact zero result depends on rounding mode + ; result_sign = if rounding == FPRounding_NEGINF then '1' else '0'; + ; result = FPZero(result_sign, N); + ; else + ; result = FPRound(result_value, fpcr, rounding, fpexc, N); + (let ((result (fp.add x y))) + (if (fp.isZero result) + (fp.+zero (widthof result)) + result + ) + ) + ; + ; if fpexc then FPProcessDenorms(type1, type2, N, fpcr); + ; return result; + )))) + ) + )) + ) +) + +;; FPSub: Floating point subtraction +(macro (FPSub x y fpcr) + ; // FPSub() + ; // ======= + ; + ; bits(N) FPSub(bits(N) op1, bits(N) op2, FPCR_Type fpcr, boolean fpexc) + ; + ; assert N IN {16,32,64}; + ; rounding = FPRoundingMode(fpcr); + ; + ; (type1,sign1,value1) = FPUnpack(op1, fpcr, fpexc); + ; (type2,sign2,value2) = FPUnpack(op2, fpcr, fpexc); + (let + ( + (sign1 (fp.isNegative x)) + (sign2 (fp.isNegative y)) + ) + ; + ; (done,result) = FPProcessNaNs(type1, type2, op1, op2, fpcr, fpexc); + (if (fp.isNaN x) + (FPProcessNaN! x) + (if (fp.isNaN y) + (FPProcessNaN! y) + ; if !done then + ; inf1 = (type1 == FPType_Infinity); inf2 = (type2 == FPType_Infinity); + ; zero1 = (type1 == FPType_Zero); zero2 = (type2 == FPType_Zero); + (let + ( + (inf1 (fp.isInfinite x)) + (inf2 (fp.isInfinite y)) + (zero1 (fp.isZero x)) + (zero2 (fp.isZero y)) + ) + ; if inf1 && inf2 && sign1 == sign2 then + ; result = FPDefaultNaN(fpcr, N); + ; if fpexc then FPProcessException(FPExc_InvalidOp, fpcr); + (if (and inf1 inf2 (= sign1 sign2)) + (FPDefaultNaN! (widthof x)) + ; elsif (inf1 && sign1 == '0') || (inf2 && sign2 == '1') then + ; result = FPInfinity('0', N); + (if (or (and inf1 (not sign1)) (and inf2 sign2)) + (fp.+oo (widthof x)) + ; elsif (inf1 && sign1 == '1') || (inf2 && sign2 == '0') then + ; result = FPInfinity('1', N); + (if (or (and inf1 sign1) (and inf2 (not sign2))) + (fp.-oo (widthof x)) + ; elsif zero1 && zero2 && sign1 == NOT(sign2) then + ; result = FPZero(sign1, N); + (if (and zero1 zero2 (= sign1 (not sign2))) + (fp_signed_zero! sign1 (widthof x)) + ; else + ; result_value = value1 - value2; + ; if result_value == 0.0 then // Sign of exact zero result depends on rounding mode + ; result_sign = if rounding == FPRounding_NEGINF then '1' else '0'; + ; result = FPZero(result_sign, N); + ; else + ; result = FPRound(result_value, fpcr, rounding, fpexc, N); + (let ((result (fp.sub x y))) + (if (fp.isZero result) + (fp.+zero (widthof result)) + result + ) + ) + ; + ; if fpexc then FPProcessDenorms(type1, type2, N, fpcr); + ; return result; + )))) + ) + )) + ) +) + +;; FPMul: Floating point multiplication +(macro (FPMul x y fpcr) + ; // FPMul() + ; // ======= + ; + ; bits(N) FPMul(bits(N) op1, bits(N) op2, FPCR_Type fpcr) + ; + ; assert N IN {16,32,64}; + ; + ; (type1,sign1,value1) = FPUnpack(op1, fpcr); + ; (type2,sign2,value2) = FPUnpack(op2, fpcr); + (let + ( + (sign1 (fp.isNegative x)) + (sign2 (fp.isNegative y)) + (N (widthof x)) + ) + ; + ; (done,result) = FPProcessNaNs(type1, type2, op1, op2, fpcr); + (if (fp.isNaN x) + (FPProcessNaN! x) + (if (fp.isNaN y) + (FPProcessNaN! y) + ; if !done then + ; inf1 = (type1 == FPType_Infinity); inf2 = (type2 == FPType_Infinity); + ; zero1 = (type1 == FPType_Zero); zero2 = (type2 == FPType_Zero); + (let + ( + (inf1 (fp.isInfinite x)) + (inf2 (fp.isInfinite y)) + (zero1 (fp.isZero x)) + (zero2 (fp.isZero y)) + ) + ; if (inf1 && zero2) || (zero1 && inf2) then + ; result = FPDefaultNaN(fpcr, N); + ; FPProcessException(FPExc_InvalidOp, fpcr); + (if (or (and inf1 zero2) (and zero1 inf2)) + (FPDefaultNaN! N) + ; elsif inf1 || inf2 then + ; result = FPInfinity(sign1 EOR sign2, N); + (if (or inf1 inf2) + (fp_signed_inf! (xor! sign1 sign2) N) + ; elsif zero1 || zero2 then + ; result = FPZero(sign1 EOR sign2, N); + (if (or zero1 zero2) + (fp_signed_zero! (xor! sign1 sign2) N) + ; else + ; result = FPRound(value1*value2, fpcr, N); + (fp.mul x y) + ; + ; FPProcessDenorms(type1, type2, N, fpcr); + ; return result; + ))) + ) + )) + ) +) + +;; FPDiv: Floating point division +(macro (FPDiv x y fpcr) + ; // FPDiv() + ; // ======= + ; + ; bits(N) FPDiv(bits(N) op1, bits(N) op2, FPCR_Type fpcr) + ; + ; assert N IN {16,32,64}; + ; + ; (type1,sign1,value1) = FPUnpack(op1, fpcr); + ; (type2,sign2,value2) = FPUnpack(op2, fpcr); + (let + ( + (sign1 (fp.isNegative x)) + (sign2 (fp.isNegative y)) + (N (widthof x)) + ) + ; + ; (done,result) = FPProcessNaNs(type1, type2, op1, op2, fpcr); + (if (fp.isNaN x) + (FPProcessNaN! x) + (if (fp.isNaN y) + (FPProcessNaN! y) + ; if !done then + ; inf1 = (type1 == FPType_Infinity); inf2 = (type2 == FPType_Infinity); + ; zero1 = (type1 == FPType_Zero); zero2 = (type2 == FPType_Zero); + (let + ( + (inf1 (fp.isInfinite x)) + (inf2 (fp.isInfinite y)) + (zero1 (fp.isZero x)) + (zero2 (fp.isZero y)) + ) + ; if (inf1 && inf2) || (zero1 && zero2) then + ; result = FPDefaultNaN(fpcr, N); + ; FPProcessException(FPExc_InvalidOp, fpcr); + (if (or (and inf1 inf2) (and zero1 zero2)) + (FPDefaultNaN! N) + ; elsif inf1 || zero2 then + ; result = FPInfinity(sign1 EOR sign2, N); + ; TODO: FPExc_DivideByZero + ; if !inf1 then FPProcessException(FPExc_DivideByZero, fpcr); + (if (or inf1 zero2) + (fp_signed_inf! (xor! sign1 sign2) N) + ; elsif zero1 || inf2 then + ; result = FPZero(sign1 EOR sign2, N); + (if (or zero1 inf2) + (fp_signed_zero! (xor! sign1 sign2) N) + ; else + ; result = FPRound(value1/value2, fpcr, N); + (fp.div x y) + ; + ; if !zero2 then + ; FPProcessDenorms(type1, type2, N, fpcr); + ; return result; + ))) + ) + )) + ) +) + +;; FPMax: Floating point maximum +; Note: ignoring the alternative floating point behavior for now +(macro (FPMax value1 value2 fpcr) + ; bits(N) FPMax(bits(N) op1, bits(N) op2, FPCR_Type fpcr) + ; boolean altfp = IsFeatureImplemented(FEAT_AFP) && !UsingAArch32() && fpcr.AH == '1'; + ; boolean fpexc = TRUE; + ; return FPMax(op1, op2, fpcr, altfp, fpexc); + + ; // FPMax() + ; // ======= + + ; bits(N) FPMax(bits(N) op1, bits(N) op2, FPCR_Type fpcr, boolean altfp) + ; boolean fpexc = TRUE; + ; return FPMax(op1, op2, fpcr, altfp, fpexc); + + ; // FPMax() + ; // ======= + ; // Compare two inputs and return the larger value after rounding. The + ; // 'fpcr' argument supplies the FPCR control bits and 'altfp' determines + ; // if the function should use alternative floating-point behavior. + + ; bits(N) FPMax(bits(N) op1, bits(N) op2, FPCR_Type fpcr_in, boolean altfp, boolean fpexc) + ; // FPMax() + ; // ====== + ; + ; bits(N) FPMax(bits(N) op1, bits(N) op2, FPCR_Type fpcr, boolean fpexc) + ; + ; assert N IN {16,32,64}; + ; boolean done; + ; bits(N) result; + ; FPCR_Type fpcr = fpcr_in; + ; (type1,sign1,value1) = FPUnpack(op1, fpcr, fpexc); + ; (type2,sign2,value2) = FPUnpack(op2, fpcr, fpexc); + + ; if altfp && type1 == FPType_Zero && type2 == FPType_Zero && sign1 != sign2 then + ; // Alternate handling of zeros with differing sign + ; return FPZero(sign2, N); + ; elsif altfp && (type1 IN {FPType_SNaN, FPType_QNaN} || type2 IN {FPType_SNaN, FPType_QNaN}) then + ; // Alternate handling of NaN inputs + ; if fpexc then FPProcessException(FPExc_InvalidOp, fpcr); + ; return (if type2 == FPType_Zero then FPZero(sign2, N) else op2); + (let + ( + (sign1 (fp.isNegative value1)) + (sign2 (fp.isNegative value2)) + (N (widthof value1)) + ) + ; + ; (done,result) = FPProcessNaNs(type1, type2, op1, op2, fpcr, fpexc); + (if (fp.isNaN value1) + (FPProcessNaN! value1) + (if (fp.isNaN value2) + (FPProcessNaN! value2) + ; if !done then + ; if value1 > value2 then + ; (fptype,sign,value) = (type1,sign1,value1); + ; else + ; (fptype,sign,value) = (type2,sign2,value2); + (let + ( + (sign (if (fp.gt value1 value2) sign1 sign2)) + (value (if (fp.gt value1 value2) value1 value2)) + (inf (fp.isInfinite value)) + (zero (fp.isZero value)) + ) + ; if fptype == FPType_Infinity then + ; result = FPInfinity(sign, N); + (if inf + (fp_signed_inf! sign N) + ; elsif fptype == FPType_Zero then + ; sign = sign1 AND sign2; // Use most positive sign + ; result = FPZero(sign, N); + (if zero + (fp_signed_zero! (and sign1 sign2) N) + ; else + ; // The use of FPRound() covers the case where there is a trapped underflow exception + ; // for a denormalized number even though the result is exact. + ; rounding = FPRoundingMode(fpcr); + ; if altfp then // Denormal output is not flushed to zero + ; fpcr.FZ = '0'; + ; fpcr.FZ16 = '0'; + ; result = FPRound(value, fpcr, rounding, fpexc, N); + ; if fpexc then FPProcessDenorms(type1, type2, N, fpcr); + value + ; return result; + )) + ) + )) + ) +) + + +;; FPMin: Floating point minimum +; Note: ignoring the alternative floating point behavior for now +(macro (FPMin value1 value2 fpcr) + ; bits(N) FPMin(bits(N) op1, bits(N) op2, FPCR_Type fpcr) + ; boolean altfp = IsFeatureImplemented(FEAT_AFP) && !UsingAArch32() && fpcr.AH == '1'; + ; boolean fpexc = TRUE; + ; return FPMin(op1, op2, fpcr, altfp, fpexc); + + ; // FPMin() + ; // ======= + + ; bits(N) FPMin(bits(N) op1, bits(N) op2, FPCR_Type fpcr, boolean altfp) + ; boolean fpexc = TRUE; + ; return FPMin(op1, op2, fpcr, altfp, fpexc); + + ; // FPMin() + ; // ======= + ; // Compare two inputs and return the larger value after rounding. The + ; // 'fpcr' argument supplies the FPCR control bits and 'altfp' determines + ; // if the function should use alternative floating-point behavior. + + ; bits(N) FPMin(bits(N) op1, bits(N) op2, FPCR_Type fpcr_in, boolean altfp, boolean fpexc) + ; // FPMin() + ; // ====== + ; + ; bits(N) FPMin(bits(N) op1, bits(N) op2, FPCR_Type fpcr, boolean fpexc) + ; + ; assert N IN {16,32,64}; + ; boolean done; + ; bits(N) result; + ; FPCR_Type fpcr = fpcr_in; + ; (type1,sign1,value1) = FPUnpack(op1, fpcr, fpexc); + ; (type2,sign2,value2) = FPUnpack(op2, fpcr, fpexc); + + ; if altfp && type1 == FPType_Zero && type2 == FPType_Zero && sign1 != sign2 then + ; // Alternate handling of zeros with differing sign + ; return FPZero(sign2, N); + ; elsif altfp && (type1 IN {FPType_SNaN, FPType_QNaN} || type2 IN {FPType_SNaN, FPType_QNaN}) then + ; // Alternate handling of NaN inputs + ; if fpexc then FPProcessException(FPExc_InvalidOp, fpcr); + ; return (if type2 == FPType_Zero then FPZero(sign2, N) else op2); + (let + ( + (sign1 (fp.isNegative value1)) + (sign2 (fp.isNegative value2)) + (N (widthof value1)) + ) + ; + ; (done,result) = FPProcessNaNs(type1, type2, op1, op2, fpcr, fpexc); + (if (fp.isNaN value1) + (FPProcessNaN! value1) + (if (fp.isNaN value2) + (FPProcessNaN! value2) + ; if !done then + ; if value1 < value2 then + ; (fptype,sign,value) = (type1,sign1,value1); + ; else + ; (fptype,sign,value) = (type2,sign2,value2); + (let + ( + (sign (if (fp.lt value1 value2) sign1 sign2)) + (value (if (fp.lt value1 value2) value1 value2)) + (inf (fp.isInfinite value)) + (zero (fp.isZero value)) + ) + ; if fptype == FPType_Infinity then + ; result = FPInfinity(sign, N); + (if inf + (fp_signed_inf! sign N) + ; elsif fptype == FPType_Zero then + ; sign = sign1 OR sign2; // Use most negative sign + ; result = FPZero(sign, N); + (if zero + (fp_signed_zero! (or sign1 sign2) N) + ; else + ; // The use of FPRound() covers the case where there is a trapped underflow exception + ; // for a denormalized number even though the result is exact. + ; rounding = FPRoundingMode(fpcr); + ; if altfp then // Denormal output is not flushed to zero + ; fpcr.FZ = '0'; + ; fpcr.FZ16 = '0'; + ; result = FPRound(value, fpcr, rounding, fpexc, N); + ; if fpexc then FPProcessDenorms(type1, type2, N, fpcr); + value + ; return result; + )) + ) + )) + ) +) + +;; FPCompare: Floating point comparison +(macro (FPCompare value1 value2 signal_nans fpcr) + ; bits(4) FPCompare(bits(N) op1, bits(N) op2, boolean signal_nans, FPCR_Type fpcr) + ; assert N IN {16,32,64}; + ; (type1,sign1,value1) = FPUnpack(op1, fpcr); + ; (type2,sign2,value2) = FPUnpack(op2, fpcr); + ; bits(4) result; + ; if type1 IN {FPType_SNaN, FPType_QNaN} || type2 IN {FPType_SNaN, FPType_QNaN} then + ; result = '0011'; + ; if type1 == FPType_SNaN || type2 == FPType_SNaN || signal_nans then + ; FPProcessException(FPExc_InvalidOp, fpcr); + (if (or (fp.isNaN value1) (fp.isNaN value2)) + (if signal_nans ; signal_nans is always false, but we need to read + #b0011 + #b0011) + ; else + ; // All non-NaN cases can be evaluated on the values produced by FPUnpack() + ; if value1 == value2 then + ; result = '0110'; + (if (fp.eq value1 value2) + #b0110 + + ; elsif value1 < value2 then + ; result = '1000'; + (if (fp.lt value1 value2) + #b1000 + ; else // value1 > value2 + ; result = '0010'; + #b0010 + ; FPProcessDenorms(type1, type2, N, fpcr); + ; return result; + ))) +) + + +;; // Convert M-bit fixed point 'op' with FBITS fractional bits to +;; // N-bit precision floating point, controlled by UNSIGNED and ROUNDING. +(macro (FixedToFP op fbits unsigned fpcr rounding M N) +; bits(N) FixedToFP(bits(M) op, integer fbits, boolean unsigned, FPCRType fpcr, FPRounding rounding) +; assert N IN {16,32,64}; +; assert M IN {16,32,64}; +; bits(N) result; +; assert fbits >= 0; +; assert rounding != FPRounding_ODD; +; // Correct signed-ness +; int_operand = Int(op, unsigned); +; // Scale by fractional bits and generate a real value +; real_operand = Real(int_operand) / 2.0^fbits; +; if real_operand == 0.0 then +; result = FPZero('0'); +; else +; result = FPRound(real_operand, fpcr, rounding); +; return result; + +;; Use the SMTLIB conversion functions to accomplish the same behavior + (let + ( + (result + (if unsigned + (to_fp_unsigned N (conv_to N (zero_ext 64 op))) + (to_fp N (conv_to N (sign_ext 64 op))))) + ) + (if (and + ;; fbits of 0 means we can use pure integer logic + (= 0 fbits) + (= rounding rounding) + ) + result + ;; unspecified bits if assumptions not met + (with (unspecified) unspecified) + ) + ) +) + +(macro (FPToFixed op fbits unsigned fpcr rounding M N) + ; bits(M) FPToFixed(bits(N) op, integer fbits, boolean unsigned, FPCRType fpcr, FPRounding rounding) + ; assert N IN {16,32,64}; + ; assert M IN {16,32,64}; + ; assert fbits >= 0; + ; assert rounding != FPRounding_ODD; + ; // Unpack using fpcr to determine if subnormals are flushed-to-zero + ; (fptype,sign,value) = FPUnpack(op, fpcr); + ; // If NaN, set cumulative flag or take exception + ; if fptype == FPType_SNaN || fptype == FPType_QNaN then + ; FPProcessException(FPExc_InvalidOp, fpcr); + ; // Scale by fractional bits and produce integer rounded towards minus-infinity + ; value = value * 2.0^fbits; + ; int_result = RoundDown(value); + ; error = value - Real(int_result); + ; // Determine whether supplied rounding mode requires an increment + ; case rounding of + ; when FPRounding_TIEEVEN + ; round_up = (error > 0.5 || (error == 0.5 && int_result[0] == '1')); + ; when FPRounding_POSINF + ; round_up = (error != 0.0); + ; when FPRounding_NEGINF + ; round_up = FALSE; + ; when FPRounding_ZERO + ; round_up = (error != 0.0 && int_result < 0); + ; when FPRounding_TIEAWAY + ; round_up = (error > 0.5 || (error == 0.5 && int_result >= 0)); + ; if round_up then int_result = int_result + 1; + ; // Generate saturated result and exceptions + ; (result, overflow) = SatQ(int_result, M, unsigned); + ; if overflow then + ; FPProcessException(FPExc_InvalidOp, fpcr); + ; elsif error != 0.0 then + ; FPProcessException(FPExc_Inexact, fpcr); + ; return result; + (let + ( + (result + (if unsigned + (fp.to_ubv M (to_fp_from_fp M op)) + (fp.to_sbv M (to_fp_from_fp M op)))) + ) + (if (and + ;; fbits of 0 means we can use pure integer logic + (= 0 fbits) + (= rounding rounding) + ) + result + ;; unspecified bits if assumptions not met + (with (unspecified) unspecified) + ) + ) +) + +;; FPCompare: Floating point comparison +(macro (FPConvert op fpcr rounding M N) + ; bits(M) FPConvert(bits(N) op, FPCRType fpcr, FPRounding rounding) + ; assert M IN {16,32,64}; + ; assert N IN {16,32,64}; + ; bits(M) result; + ; // Unpack floating-point operand optionally with flush-to-zero. + ; (fptype,sign,value) = FPUnpackCV(op, fpcr); + (let + ( + (sign (fp.isNegative op)) + ) + ; alt_hp = (M == 16) && (fpcr.AHP == '1'); + ; if fptype == FPType_SNaN || fptype == FPType_QNaN then + ; if alt_hp then + ; result = FPZero(sign, M); + ; elsif fpcr.DN == '1' then + ; result = FPDefaultNaN(fpcr, M); + ; else + ; result = FPConvertNaN(op, M); + ; if fptype == FPType_SNaN || alt_hp then + ; FPProcessException(FPExc_InvalidOp,fpcr); + (if (fp.isNaN op) + (conv_to 64 (FPConvertNaN! op M N)) + ; else if fptype == FPType_Infinity then + ; if alt_hp then + ; result = sign:Ones(M-1); + ; FPProcessException(FPExc_InvalidOp, fpcr); + ; else + ; result = FPInfinity(sign, M); + (if (fp.isInfinite op) + (conv_to 64 (fp_signed_inf! sign M)) + ; else if fptype == FPType_Zero then + ; result = FPZero(sign, M); + (if (fp.isZero op) + (conv_to 64 (fp_signed_zero! sign M)) + ; else + ; result = FPRoundCV(value, fpcr, rounding, M); + ; FPProcessDenorm(fptype, N, fpcr); + ; return result; +;; NOTE: call out to the SMT-LIB conversion function for this logic + (if (= rounding rounding) + (conv_to 64 (to_fp_from_fp M op)) + (with (unspecified) unspecified) + ))))) +) + +(macro (FPConvertNaN x M N) + ; bits(M) FPConvertNaN(bits(N) op, integer M) + ; assert N IN {16,32,64}; + ; assert M IN {16,32,64}; + ; bits(M) result; + ; bits(51) frac; + ; sign = op; + (let + ( + (op (zero_ext 64 x)) + (sign + (if (= N 64) + (extract 63 63 op) + (extract 31 31 op) + ) + ) + ; // Unpack payload from input NaN + ; case N of + (frac (switch N + ; when 64 frac = op<50:0>; + (64 (extract 50 0 op)) + ; when 32 frac = op<21:0>:Zeros(29); + (32 (concat (extract 21 0 op) (bvzero! 29))) + ; when 16 frac = op<8:0>:Zeros(42); + (16 (concat (extract 8 0 op) (bvzero! 42))))) + ) + ; // Repack payload into output NaN, while + ; // converting an SNaN to a QNaN. + ; case M of + (switch M + ; when 64 result = sign:Ones(M-52):frac; + (64 (concat sign (bvones! 12) frac)) + ; when 32 result = sign:Ones(M-23):frac<50:29>; + (32 (conv_to 64 (concat sign (bvones! 9) (extract 50 29 frac)))) + ; when 16 result = sign:Ones(M-10):frac<50:42>; + (16 (conv_to 64 (concat sign (bvones! 6) (extract 50 42 frac)))) + ) + ; return result; + ) +) + +(macro (FPRoundInt op fpcr rounding exact) + ;bits(N) FPRoundInt(bits(N) op, FPCR_Type fpcr, FPRounding rounding, boolean exact) + ; assert rounding != FPRounding_ODD; + ; assert N IN {16,32,64}; + ; // When alternative floating-point support is TRUE, do not generate + ; // Input Denormal floating-point exceptions. + ; altfp = IsFeatureImplemented(FEAT_AFP) && !UsingAArch32() && fpcr.AH == '1'; + ; fpexc = !altfp; + ; // Unpack using FPCR to determine if subnormals are flushed-to-zero. + ; (fptype,sign,value) = FPUnpack(op, fpcr, fpexc); + (let + ( + (sign (fp.isNegative op)) + (N (widthof op)) + ) + ; bits(N) result; + ; if fptype == FPType_SNaN || fptype == FPType_QNaN then + ; result = FPProcessNaN(fptype, op, fpcr); + (if (fp.isNaN op) + (FPProcessNaN! op) + ; elsif fptype == FPType_Infinity then + ; result = FPInfinity(sign, N); + (if (fp.isInfinite op) + (fp_signed_inf! sign N) + ; elsif fptype == FPType_Zero then + ; result = FPZero(sign, N); + (if (fp.isZero op) + (fp_signed_zero! sign N) + ; else + +;; FPRounding_TIEEVEN, FPRounding_POSINF, FPRounding_NEGINF, FPRounding_ZERO, FPRounding_TIEAWAY, FPRounding_ODD + ; // Extract integer component. + ; int_result = RoundDown(value); + ; error = value - Real(int_result); + ; // Determine whether supplied rounding mode requires an increment. + ; boolean round_up; + (let ((result + (switch rounding + ; case rounding of + ; when FPRounding_TIEEVEN + ; round_up = (error > 0.5 || (error == 0.5 && int_result<0> == '1')); + (0 (fp.nearest op)) + ; when FPRounding_POSINF + ; round_up = (error != 0.0); + (1 (fp.ceil op)) + ; when FPRounding_NEGINF + ; round_up = FALSE; + (2 (fp.floor op)) + ; when FPRounding_ZERO + ; round_up = (error != 0.0 && int_result < 0); + (3 (fp.trunc op)) + ; when FPRounding_TIEAWAY + ; round_up = (error > 0.5 || (error == 0.5 && int_result >= 0)); + ) + ; if round_up then int_result = int_result + 1; + ; // Convert integer value into an equivalent real value. + ; real_result = Real(int_result); + ; // Re-encode as a floating-point value, result is always exact. + ; if real_result == 0.0 then + ; result = FPZero(sign, N); + ; else + ; result = FPRound(real_result, fpcr, FPRounding_ZERO, N); + ; // Generate inexact exceptions. + ; if error != 0.0 && exact then + ; FPProcessException(FPExc_Inexact, fpcr); + ; return result; + )) + (if exact + (with (unspecified) unspecified) + result)) + )))) +) + +(macro (FPSqrt op fpcr) +; bits(N) FPSqrt(bits(N) op, FPCRType fpcr) + ; assert N IN {16,32,64}; + ; (fptype,sign,value) = FPUnpack(op, fpcr); + ; if fptype == FPType_SNaN || fptype == FPType_QNaN then + (let + ( + (sign (fp.isNegative op)) + (N (widthof op)) + ) + (if (fp.isNaN op) + (FPProcessNaN! op) + ; result = FPProcessNaN(fptype, op, fpcr); + ; elsif fptype == FPType_Zero then + ; result = FPZero(sign); + (if (fp.isZero op) + (fp_signed_zero! sign N) + ; elsif fptype == FPType_Infinity && sign == '0' then + ; result = FPInfinity(sign); + (if (and (fp.isInfinite op) (not sign)) + (fp.+oo N) + ; elsif sign == '1' then + ; result = FPDefaultNaN(); + ; FPProcessException(FPExc_InvalidOp, fpcr); + (if sign + (nan_canon! N) + ; else + ; result = FPRound(Sqrt(value), fpcr); + (fp.sqrt op)))))) + ; return result; +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/state.isle b/cranelift/codegen/src/isa/aarch64/spec/state.isle new file mode 100644 index 000000000000..305f16469613 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/state.isle @@ -0,0 +1,44 @@ +; Parameters of a load operation. +(state isa_load + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)) + ) + ) + (default + (not (:active isa_load)) + ) +) + +; Parameters of a store operation. +(state isa_store + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)) + (value (bv 64)) + ) + ) + (default + (and + ; Store is not active. + (not (:active isa_store)) + + ; Must provide a fixed size in the default case, otherwise type + ; inference is underconstrained. + (= (:size_bits isa_store) 1) + ) + ) +) + +; Floating point control register. +(state fpcr + ; FPCR is a 64-bit register. + (type (bv 64)) + + ; Unconstrained. We currently do not model the FPCR configuration bits. + (default true) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/stores.isle b/cranelift/codegen/src/isa/aarch64/spec/stores.isle new file mode 100644 index 000000000000..4ff67c4f96ae --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/stores.isle @@ -0,0 +1,673 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.Store8 (tag isaspec_generated)) + +(spec + (MInst.Store8 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn rm)) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn rm)) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 8) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 11 0 uimm12)))) + (= (conv_to 8 (:value isa_store)) (extract 7 0 (as rd (bv 64)))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.Store16 (tag isaspec_generated)) + +(spec + (MInst.Store16 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn rm)) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (concat (extract 62 0 rm) #b0))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b0)))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b0)))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 62 0 rm) #b0)))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 16) + (= (:addr isa_store) (bvadd rn (concat (zero_ext 63 (extract 11 0 uimm12)) #b0))) + (= (conv_to 16 (:value isa_store)) (extract 15 0 (as rd (bv 64)))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.Store32 (tag isaspec_generated)) + +(spec + (MInst.Store32 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn rm)) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (concat (extract 61 0 rm) #b00))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b00)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b00)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 61 0 rm) #b00)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (concat (zero_ext 62 (extract 11 0 uimm12)) #b00))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (as rd (bv 64)))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.Store64 (tag isaspec_generated)) + +(spec + (MInst.Store64 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn rm)) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (concat (extract 60 0 rm) #b000))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b000)))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b000)))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 60 0 rm) #b000)))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (concat (zero_ext 61 (extract 11 0 uimm12)) #b000))) + (= (conv_to 64 (:value isa_store)) (as rd (bv 64))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.FpuStore32 (tag isaspec_generated)) + +(spec + (MInst.FpuStore32 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn rm)) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (concat (extract 61 0 rm) #b00))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b00)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b00)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 61 0 rm) #b00)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 32) + (= (:addr isa_store) (bvadd rn (concat (zero_ext 62 (extract 11 0 uimm12)) #b00))) + (= (conv_to 32 (:value isa_store)) (extract 31 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) + +(attr MInst.FpuStore64 (tag isaspec_generated)) + +(spec + (MInst.FpuStore64 rd mem flags) + (provide + (match + mem + ((RegReg rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn rm)) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((RegScaled rn rm) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (concat (extract 60 0 rm) #b000))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((RegScaledExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (concat (extract 31 0 rm) #b000)))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 31 0 rm) #b000)))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (concat (extract 60 0 rm) #b000)))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ) + ) + ((RegExtended rn rm extendop) + (match + extendop + ((UXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (zero_ext 64 (extract 31 0 rm)))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTW) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 31 0 rm)))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((SXTX) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 rm))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ) + ) + ((Unscaled rn simm9) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (sign_ext 64 (extract 8 0 simm9)))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ((UnsignedOffset rn uimm12) + (and + (= (:active isa_store) true) + (= (:size_bits isa_store) 64) + (= (:addr isa_store) (bvadd rn (concat (zero_ext 61 (extract 11 0 uimm12)) #b000))) + (= (conv_to 64 (:value isa_store)) (extract 63 0 (conv_to 128 (as rd (bv 64))))) + ) + ) + ) + ) + (require + (match + mem + ((RegReg rn rm) true) + ((RegScaled rn rm) true) + ((RegScaledExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((RegExtended rn rm extendop) (match extendop ((UXTW) true) ((SXTW) true) ((SXTX) true))) + ((Unscaled rn simm9) true) + ((UnsignedOffset rn uimm12) true) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/vec_dup_imm.isle b/cranelift/codegen/src/isa/aarch64/spec/vec_dup_imm.isle new file mode 100644 index 000000000000..ac24caa06a61 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/vec_dup_imm.isle @@ -0,0 +1,93 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.VecDupImm (tag isaspec_generated)) + +(spec + (MInst.VecDupImm rd imm invert size) + (provide + (match + size + ((Size32x2) + (and + (=> + (not invert) + (= + rd + (zero_ext + 128 + (replicate + (replicate + (zero_ext + 32 + (concat + (concat + (concat + (concat + (concat + (concat + (concat (extract 2 2 (extract 7 5 (:imm imm))) (extract 1 1 (extract 7 5 (:imm imm)))) + (extract 0 0 (extract 7 5 (:imm imm))) + ) + (extract 4 4 (extract 4 0 (:imm imm))) + ) + (extract 3 3 (extract 4 0 (:imm imm))) + ) + (extract 2 2 (extract 4 0 (:imm imm))) + ) + (extract 1 1 (extract 4 0 (:imm imm))) + ) + (extract 0 0 (extract 4 0 (:imm imm))) + ) + ) + 2 + ) + 1 + ) + ) + ) + ) + (=> + invert + (= + rd + (zero_ext + 128 + (bvnot + (replicate + (replicate + (zero_ext + 32 + (concat + (concat + (concat + (concat + (concat + (concat + (concat (extract 2 2 (extract 7 5 (:imm imm))) (extract 1 1 (extract 7 5 (:imm imm)))) + (extract 0 0 (extract 7 5 (:imm imm))) + ) + (extract 4 4 (extract 4 0 (:imm imm))) + ) + (extract 3 3 (extract 4 0 (:imm imm))) + ) + (extract 2 2 (extract 4 0 (:imm imm))) + ) + (extract 1 1 (extract 4 0 (:imm imm))) + ) + (extract 0 0 (extract 4 0 (:imm imm))) + ) + ) + 2 + ) + 1 + ) + ) + ) + ) + ) + ) + ) + ) + ) + (require (match size ((Size32x2) (or (not invert) invert)))) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/vec_lanes.isle b/cranelift/codegen/src/isa/aarch64/spec/vec_lanes.isle new file mode 100644 index 000000000000..393b226491c5 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/vec_lanes.isle @@ -0,0 +1,303 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.VecLanes (tag isaspec_generated)) + +(spec + (MInst.VecLanes op rd rn size) + (provide + (match + size + ((Size32x4) + (match + op + ((Uminv) + (with + (t1 t10 t11 t12 t2 t3 t4 t5 t6 t7 t8 t9) + (and + (= t1 (bvsle (zero_ext 64 (extract 31 0 rn)) (zero_ext 64 (extract 63 32 rn)))) + (if t1 (= t2 (extract 31 0 rn)) (= t3 (extract 63 32 rn))) + (= t4 (if t1 t2 t3)) + (= t5 (bvsle (zero_ext 64 t4) (zero_ext 64 (extract 95 64 rn)))) + (if t5 (= t6 t4) (= t7 (extract 95 64 rn))) + (= t8 (if t5 t6 t7)) + (= t9 (bvsle (zero_ext 64 t8) (zero_ext 64 (extract 127 96 rn)))) + (if t9 (= t10 t8) (= t11 (extract 127 96 rn))) + (= t12 (if t9 t10 t11)) + (= rd (zero_ext 128 t12)) + ) + ) + ) + ((Addv) + (= + rd + (zero_ext 128 (bvadd (bvadd (extract 31 0 rn) (extract 63 32 rn)) (bvadd (extract 95 64 rn) (extract 127 96 rn)))) + ) + ) + ) + ) + ((Size16x8) + (match + op + ((Uminv) + (with + (t1 t10 t11 t12 t13 t14 t15 t16 t17 t18 t19 t2 t20 t21 t22 t23 t24 t25 t26 t27 t28 t3 t4 t5 t6 t7 t8 t9) + (and + (= t1 (bvsle (zero_ext 32 (extract 15 0 rn)) (zero_ext 32 (extract 31 16 rn)))) + (if t1 (= t2 (extract 15 0 rn)) (= t3 (extract 31 16 rn))) + (= t4 (if t1 t2 t3)) + (= t5 (bvsle (zero_ext 32 t4) (zero_ext 32 (extract 47 32 rn)))) + (if t5 (= t6 t4) (= t7 (extract 47 32 rn))) + (= t8 (if t5 t6 t7)) + (= t9 (bvsle (zero_ext 32 t8) (zero_ext 32 (extract 63 48 rn)))) + (if t9 (= t10 t8) (= t11 (extract 63 48 rn))) + (= t12 (if t9 t10 t11)) + (= t13 (bvsle (zero_ext 32 t12) (zero_ext 32 (extract 79 64 rn)))) + (if t13 (= t14 t12) (= t15 (extract 79 64 rn))) + (= t16 (if t13 t14 t15)) + (= t17 (bvsle (zero_ext 32 t16) (zero_ext 32 (extract 95 80 rn)))) + (if t17 (= t18 t16) (= t19 (extract 95 80 rn))) + (= t20 (if t17 t18 t19)) + (= t21 (bvsle (zero_ext 32 t20) (zero_ext 32 (extract 111 96 rn)))) + (if t21 (= t22 t20) (= t23 (extract 111 96 rn))) + (= t24 (if t21 t22 t23)) + (= t25 (bvsle (zero_ext 32 t24) (zero_ext 32 (extract 127 112 rn)))) + (if t25 (= t26 t24) (= t27 (extract 127 112 rn))) + (= t28 (if t25 t26 t27)) + (= rd (zero_ext 128 t28)) + ) + ) + ) + ((Addv) + (= + rd + (zero_ext + 128 + (bvadd + (bvadd (bvadd (extract 15 0 rn) (extract 31 16 rn)) (bvadd (extract 47 32 rn) (extract 63 48 rn))) + (bvadd (bvadd (extract 79 64 rn) (extract 95 80 rn)) (bvadd (extract 111 96 rn) (extract 127 112 rn))) + ) + ) + ) + ) + ) + ) + ((Size16x4) + (match + op + ((Uminv) + (with + (t1 t10 t11 t12 t2 t3 t4 t5 t6 t7 t8 t9) + (and + (= t1 (bvsle (zero_ext 32 (extract 15 0 rn)) (zero_ext 32 (extract 31 16 rn)))) + (if t1 (= t2 (extract 15 0 rn)) (= t3 (extract 31 16 rn))) + (= t4 (if t1 t2 t3)) + (= t5 (bvsle (zero_ext 32 t4) (zero_ext 32 (extract 47 32 rn)))) + (if t5 (= t6 t4) (= t7 (extract 47 32 rn))) + (= t8 (if t5 t6 t7)) + (= t9 (bvsle (zero_ext 32 t8) (zero_ext 32 (extract 63 48 rn)))) + (if t9 (= t10 t8) (= t11 (extract 63 48 rn))) + (= t12 (if t9 t10 t11)) + (= rd (zero_ext 128 t12)) + ) + ) + ) + ((Addv) + (= rd (zero_ext 128 (bvadd (bvadd (extract 15 0 rn) (extract 31 16 rn)) (bvadd (extract 47 32 rn) (extract 63 48 rn))))) + ) + ) + ) + ((Size8x16) + (match + op + ((Uminv) + (with + (t1 + t10 + t11 + t12 + t13 + t14 + t15 + t16 + t17 + t18 + t19 + t2 + t20 + t21 + t22 + t23 + t24 + t25 + t26 + t27 + t28 + t29 + t3 + t30 + t31 + t32 + t33 + t34 + t35 + t36 + t37 + t38 + t39 + t4 + t40 + t41 + t42 + t43 + t44 + t45 + t46 + t47 + t48 + t49 + t5 + t50 + t51 + t52 + t53 + t54 + t55 + t56 + t57 + t58 + t59 + t6 + t60 + t7 + t8 + t9 + ) + (and + (= t1 (bvsle (zero_ext 16 (extract 7 0 rn)) (zero_ext 16 (extract 15 8 rn)))) + (if t1 (= t2 (extract 7 0 rn)) (= t3 (extract 15 8 rn))) + (= t4 (if t1 t2 t3)) + (= t5 (bvsle (zero_ext 16 t4) (zero_ext 16 (extract 23 16 rn)))) + (if t5 (= t6 t4) (= t7 (extract 23 16 rn))) + (= t8 (if t5 t6 t7)) + (= t9 (bvsle (zero_ext 16 t8) (zero_ext 16 (extract 31 24 rn)))) + (if t9 (= t10 t8) (= t11 (extract 31 24 rn))) + (= t12 (if t9 t10 t11)) + (= t13 (bvsle (zero_ext 16 t12) (zero_ext 16 (extract 39 32 rn)))) + (if t13 (= t14 t12) (= t15 (extract 39 32 rn))) + (= t16 (if t13 t14 t15)) + (= t17 (bvsle (zero_ext 16 t16) (zero_ext 16 (extract 47 40 rn)))) + (if t17 (= t18 t16) (= t19 (extract 47 40 rn))) + (= t20 (if t17 t18 t19)) + (= t21 (bvsle (zero_ext 16 t20) (zero_ext 16 (extract 55 48 rn)))) + (if t21 (= t22 t20) (= t23 (extract 55 48 rn))) + (= t24 (if t21 t22 t23)) + (= t25 (bvsle (zero_ext 16 t24) (zero_ext 16 (extract 63 56 rn)))) + (if t25 (= t26 t24) (= t27 (extract 63 56 rn))) + (= t28 (if t25 t26 t27)) + (= t29 (bvsle (zero_ext 16 t28) (zero_ext 16 (extract 71 64 rn)))) + (if t29 (= t30 t28) (= t31 (extract 71 64 rn))) + (= t32 (if t29 t30 t31)) + (= t33 (bvsle (zero_ext 16 t32) (zero_ext 16 (extract 79 72 rn)))) + (if t33 (= t34 t32) (= t35 (extract 79 72 rn))) + (= t36 (if t33 t34 t35)) + (= t37 (bvsle (zero_ext 16 t36) (zero_ext 16 (extract 87 80 rn)))) + (if t37 (= t38 t36) (= t39 (extract 87 80 rn))) + (= t40 (if t37 t38 t39)) + (= t41 (bvsle (zero_ext 16 t40) (zero_ext 16 (extract 95 88 rn)))) + (if t41 (= t42 t40) (= t43 (extract 95 88 rn))) + (= t44 (if t41 t42 t43)) + (= t45 (bvsle (zero_ext 16 t44) (zero_ext 16 (extract 103 96 rn)))) + (if t45 (= t46 t44) (= t47 (extract 103 96 rn))) + (= t48 (if t45 t46 t47)) + (= t49 (bvsle (zero_ext 16 t48) (zero_ext 16 (extract 111 104 rn)))) + (if t49 (= t50 t48) (= t51 (extract 111 104 rn))) + (= t52 (if t49 t50 t51)) + (= t53 (bvsle (zero_ext 16 t52) (zero_ext 16 (extract 119 112 rn)))) + (if t53 (= t54 t52) (= t55 (extract 119 112 rn))) + (= t56 (if t53 t54 t55)) + (= t57 (bvsle (zero_ext 16 t56) (zero_ext 16 (extract 127 120 rn)))) + (if t57 (= t58 t56) (= t59 (extract 127 120 rn))) + (= t60 (if t57 t58 t59)) + (= rd (zero_ext 128 t60)) + ) + ) + ) + ((Addv) + (= + rd + (zero_ext + 128 + (bvadd + (bvadd + (bvadd (bvadd (extract 7 0 rn) (extract 15 8 rn)) (bvadd (extract 23 16 rn) (extract 31 24 rn))) + (bvadd (bvadd (extract 39 32 rn) (extract 47 40 rn)) (bvadd (extract 55 48 rn) (extract 63 56 rn))) + ) + (bvadd + (bvadd (bvadd (extract 71 64 rn) (extract 79 72 rn)) (bvadd (extract 87 80 rn) (extract 95 88 rn))) + (bvadd (bvadd (extract 103 96 rn) (extract 111 104 rn)) (bvadd (extract 119 112 rn) (extract 127 120 rn))) + ) + ) + ) + ) + ) + ) + ) + ((Size8x8) + (match + op + ((Uminv) + (with + (t1 t10 t11 t12 t13 t14 t15 t16 t17 t18 t19 t2 t20 t21 t22 t23 t24 t25 t26 t27 t28 t3 t4 t5 t6 t7 t8 t9) + (and + (= t1 (bvsle (zero_ext 16 (extract 7 0 rn)) (zero_ext 16 (extract 15 8 rn)))) + (if t1 (= t2 (extract 7 0 rn)) (= t3 (extract 15 8 rn))) + (= t4 (if t1 t2 t3)) + (= t5 (bvsle (zero_ext 16 t4) (zero_ext 16 (extract 23 16 rn)))) + (if t5 (= t6 t4) (= t7 (extract 23 16 rn))) + (= t8 (if t5 t6 t7)) + (= t9 (bvsle (zero_ext 16 t8) (zero_ext 16 (extract 31 24 rn)))) + (if t9 (= t10 t8) (= t11 (extract 31 24 rn))) + (= t12 (if t9 t10 t11)) + (= t13 (bvsle (zero_ext 16 t12) (zero_ext 16 (extract 39 32 rn)))) + (if t13 (= t14 t12) (= t15 (extract 39 32 rn))) + (= t16 (if t13 t14 t15)) + (= t17 (bvsle (zero_ext 16 t16) (zero_ext 16 (extract 47 40 rn)))) + (if t17 (= t18 t16) (= t19 (extract 47 40 rn))) + (= t20 (if t17 t18 t19)) + (= t21 (bvsle (zero_ext 16 t20) (zero_ext 16 (extract 55 48 rn)))) + (if t21 (= t22 t20) (= t23 (extract 55 48 rn))) + (= t24 (if t21 t22 t23)) + (= t25 (bvsle (zero_ext 16 t24) (zero_ext 16 (extract 63 56 rn)))) + (if t25 (= t26 t24) (= t27 (extract 63 56 rn))) + (= t28 (if t25 t26 t27)) + (= rd (zero_ext 128 t28)) + ) + ) + ) + ((Addv) + (= + rd + (zero_ext + 128 + (bvadd + (bvadd (bvadd (extract 7 0 rn) (extract 15 8 rn)) (bvadd (extract 23 16 rn) (extract 31 24 rn))) + (bvadd (bvadd (extract 39 32 rn) (extract 47 40 rn)) (bvadd (extract 55 48 rn) (extract 63 56 rn))) + ) + ) + ) + ) + ) + ) + ) + ) + (require + (match + size + ((Size32x4) (match op ((Uminv) true) ((Addv) true))) + ((Size16x8) (match op ((Uminv) true) ((Addv) true))) + ((Size16x4) (match op ((Uminv) true) ((Addv) true))) + ((Size8x16) (match op ((Uminv) true) ((Addv) true))) + ((Size8x8) (match op ((Uminv) true) ((Addv) true))) + ) + ) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/vec_misc.isle b/cranelift/codegen/src/isa/aarch64/spec/vec_misc.isle new file mode 100644 index 000000000000..6fa2323a688f --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/vec_misc.isle @@ -0,0 +1,1296 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.VecMisc (tag isaspec_generated)) + +(spec + (MInst.VecMisc op rd rn size) + (provide + (match + size + ((Size8x16) + (match + op + ((Cnt) + (with + (t0 + t10 + t100 + t101 + t102 + t103 + t104 + t105 + t106 + t107 + t108 + t109 + t11 + t110 + t111 + t112 + t113 + t114 + t115 + t116 + t117 + t118 + t119 + t12 + t120 + t121 + t122 + t123 + t124 + t125 + t126 + t127 + t128 + t129 + t13 + t130 + t131 + t132 + t133 + t134 + t135 + t136 + t137 + t138 + t139 + t14 + t140 + t141 + t142 + t143 + t144 + t145 + t146 + t147 + t148 + t149 + t15 + t150 + t151 + t152 + t153 + t154 + t155 + t156 + t157 + t158 + t159 + t16 + t160 + t161 + t162 + t163 + t164 + t165 + t166 + t167 + t168 + t169 + t17 + t170 + t171 + t172 + t173 + t174 + t175 + t176 + t177 + t178 + t179 + t18 + t180 + t181 + t182 + t183 + t184 + t185 + t186 + t187 + t188 + t189 + t19 + t190 + t191 + t192 + t193 + t194 + t195 + t196 + t197 + t198 + t199 + t2 + t20 + t200 + t201 + t202 + t203 + t204 + t205 + t206 + t207 + t208 + t209 + t21 + t210 + t211 + t212 + t213 + t214 + t215 + t216 + t217 + t218 + t219 + t22 + t220 + t221 + t222 + t223 + t224 + t225 + t226 + t227 + t228 + t229 + t23 + t230 + t231 + t232 + t233 + t234 + t235 + t236 + t237 + t238 + t239 + t24 + t240 + t241 + t242 + t243 + t244 + t245 + t246 + t247 + t248 + t249 + t25 + t250 + t251 + t252 + t253 + t254 + t255 + t256 + t257 + t258 + t259 + t26 + t260 + t261 + t262 + t263 + t264 + t265 + t266 + t267 + t268 + t269 + t27 + t270 + t271 + t272 + t273 + t274 + t275 + t276 + t277 + t278 + t279 + t28 + t280 + t281 + t282 + t283 + t284 + t285 + t286 + t287 + t288 + t289 + t29 + t290 + t291 + t292 + t293 + t294 + t295 + t296 + t297 + t298 + t299 + t3 + t30 + t300 + t301 + t302 + t303 + t304 + t305 + t306 + t307 + t308 + t309 + t31 + t310 + t311 + t312 + t313 + t314 + t315 + t316 + t317 + t318 + t319 + t32 + t320 + t321 + t322 + t323 + t324 + t325 + t326 + t327 + t328 + t329 + t33 + t330 + t331 + t332 + t333 + t334 + t335 + t336 + t337 + t338 + t339 + t34 + t340 + t341 + t342 + t343 + t344 + t345 + t346 + t347 + t348 + t349 + t35 + t350 + t351 + t352 + t353 + t354 + t355 + t356 + t357 + t358 + t359 + t36 + t360 + t361 + t362 + t363 + t364 + t365 + t366 + t367 + t368 + t369 + t37 + t370 + t371 + t372 + t373 + t374 + t375 + t376 + t377 + t378 + t379 + t38 + t380 + t381 + t382 + t383 + t384 + t385 + t386 + t387 + t388 + t389 + t39 + t390 + t391 + t392 + t393 + t394 + t395 + t396 + t397 + t398 + t399 + t4 + t40 + t400 + t41 + t42 + t43 + t44 + t45 + t46 + t47 + t48 + t49 + t5 + t50 + t51 + t52 + t53 + t54 + t55 + t56 + t57 + t58 + t59 + t6 + t60 + t61 + t62 + t63 + t64 + t65 + t66 + t67 + t68 + t69 + t7 + t70 + t71 + t72 + t73 + t74 + t75 + t76 + t77 + t78 + t79 + t8 + t80 + t81 + t82 + t83 + t84 + t85 + t86 + t87 + t88 + t89 + t9 + t90 + t91 + t92 + t93 + t94 + t95 + t96 + t97 + t98 + t99 + ) + (and + (= t0 #x0) + (= t2 (= (extract 0 0 rn) #b1)) + (if t2 (= t3 #x1) true) + (= t4 (if t2 t3 t0)) + (= t5 (= (extract 1 1 rn) #b1)) + (if t5 (= t6 (bvadd t4 #x1)) true) + (= t7 (if t5 t6 t4)) + (= t8 (= (extract 2 2 rn) #b1)) + (if t8 (= t9 (bvadd t7 #x1)) true) + (= t10 (if t8 t9 t7)) + (= t11 (= (extract 3 3 rn) #b1)) + (if t11 (= t12 (bvadd t10 #x1)) true) + (= t13 (if t11 t12 t10)) + (= t14 (= (extract 4 4 rn) #b1)) + (if t14 (= t15 (bvadd t13 #x1)) true) + (= t16 (if t14 t15 t13)) + (= t17 (= (extract 5 5 rn) #b1)) + (if t17 (= t18 (bvadd t16 #x1)) true) + (= t19 (if t17 t18 t16)) + (= t20 (= (extract 6 6 rn) #b1)) + (if t20 (= t21 (bvadd t19 #x1)) true) + (= t22 (if t20 t21 t19)) + (= t23 (= (extract 7 7 rn) #b1)) + (if t23 (= t24 (bvadd t22 #x1)) true) + (= t25 (if t23 t24 t22)) + (= t26 #x0) + (= t27 (= (extract 8 8 rn) #b1)) + (if t27 (= t28 #x1) true) + (= t29 (if t27 t28 t26)) + (= t30 (= (extract 9 9 rn) #b1)) + (if t30 (= t31 (bvadd t29 #x1)) true) + (= t32 (if t30 t31 t29)) + (= t33 (= (extract 10 10 rn) #b1)) + (if t33 (= t34 (bvadd t32 #x1)) true) + (= t35 (if t33 t34 t32)) + (= t36 (= (extract 11 11 rn) #b1)) + (if t36 (= t37 (bvadd t35 #x1)) true) + (= t38 (if t36 t37 t35)) + (= t39 (= (extract 12 12 rn) #b1)) + (if t39 (= t40 (bvadd t38 #x1)) true) + (= t41 (if t39 t40 t38)) + (= t42 (= (extract 13 13 rn) #b1)) + (if t42 (= t43 (bvadd t41 #x1)) true) + (= t44 (if t42 t43 t41)) + (= t45 (= (extract 14 14 rn) #b1)) + (if t45 (= t46 (bvadd t44 #x1)) true) + (= t47 (if t45 t46 t44)) + (= t48 (= (extract 15 15 rn) #b1)) + (if t48 (= t49 (bvadd t47 #x1)) true) + (= t50 (if t48 t49 t47)) + (= t51 #x0) + (= t52 (= (extract 16 16 rn) #b1)) + (if t52 (= t53 #x1) true) + (= t54 (if t52 t53 t51)) + (= t55 (= (extract 17 17 rn) #b1)) + (if t55 (= t56 (bvadd t54 #x1)) true) + (= t57 (if t55 t56 t54)) + (= t58 (= (extract 18 18 rn) #b1)) + (if t58 (= t59 (bvadd t57 #x1)) true) + (= t60 (if t58 t59 t57)) + (= t61 (= (extract 19 19 rn) #b1)) + (if t61 (= t62 (bvadd t60 #x1)) true) + (= t63 (if t61 t62 t60)) + (= t64 (= (extract 20 20 rn) #b1)) + (if t64 (= t65 (bvadd t63 #x1)) true) + (= t66 (if t64 t65 t63)) + (= t67 (= (extract 21 21 rn) #b1)) + (if t67 (= t68 (bvadd t66 #x1)) true) + (= t69 (if t67 t68 t66)) + (= t70 (= (extract 22 22 rn) #b1)) + (if t70 (= t71 (bvadd t69 #x1)) true) + (= t72 (if t70 t71 t69)) + (= t73 (= (extract 23 23 rn) #b1)) + (if t73 (= t74 (bvadd t72 #x1)) true) + (= t75 (if t73 t74 t72)) + (= t76 #x0) + (= t77 (= (extract 24 24 rn) #b1)) + (if t77 (= t78 #x1) true) + (= t79 (if t77 t78 t76)) + (= t80 (= (extract 25 25 rn) #b1)) + (if t80 (= t81 (bvadd t79 #x1)) true) + (= t82 (if t80 t81 t79)) + (= t83 (= (extract 26 26 rn) #b1)) + (if t83 (= t84 (bvadd t82 #x1)) true) + (= t85 (if t83 t84 t82)) + (= t86 (= (extract 27 27 rn) #b1)) + (if t86 (= t87 (bvadd t85 #x1)) true) + (= t88 (if t86 t87 t85)) + (= t89 (= (extract 28 28 rn) #b1)) + (if t89 (= t90 (bvadd t88 #x1)) true) + (= t91 (if t89 t90 t88)) + (= t92 (= (extract 29 29 rn) #b1)) + (if t92 (= t93 (bvadd t91 #x1)) true) + (= t94 (if t92 t93 t91)) + (= t95 (= (extract 30 30 rn) #b1)) + (if t95 (= t96 (bvadd t94 #x1)) true) + (= t97 (if t95 t96 t94)) + (= t98 (= (extract 31 31 rn) #b1)) + (if t98 (= t99 (bvadd t97 #x1)) true) + (= t100 (if t98 t99 t97)) + (= t101 #x0) + (= t102 (= (extract 32 32 rn) #b1)) + (if t102 (= t103 #x1) true) + (= t104 (if t102 t103 t101)) + (= t105 (= (extract 33 33 rn) #b1)) + (if t105 (= t106 (bvadd t104 #x1)) true) + (= t107 (if t105 t106 t104)) + (= t108 (= (extract 34 34 rn) #b1)) + (if t108 (= t109 (bvadd t107 #x1)) true) + (= t110 (if t108 t109 t107)) + (= t111 (= (extract 35 35 rn) #b1)) + (if t111 (= t112 (bvadd t110 #x1)) true) + (= t113 (if t111 t112 t110)) + (= t114 (= (extract 36 36 rn) #b1)) + (if t114 (= t115 (bvadd t113 #x1)) true) + (= t116 (if t114 t115 t113)) + (= t117 (= (extract 37 37 rn) #b1)) + (if t117 (= t118 (bvadd t116 #x1)) true) + (= t119 (if t117 t118 t116)) + (= t120 (= (extract 38 38 rn) #b1)) + (if t120 (= t121 (bvadd t119 #x1)) true) + (= t122 (if t120 t121 t119)) + (= t123 (= (extract 39 39 rn) #b1)) + (if t123 (= t124 (bvadd t122 #x1)) true) + (= t125 (if t123 t124 t122)) + (= t126 #x0) + (= t127 (= (extract 40 40 rn) #b1)) + (if t127 (= t128 #x1) true) + (= t129 (if t127 t128 t126)) + (= t130 (= (extract 41 41 rn) #b1)) + (if t130 (= t131 (bvadd t129 #x1)) true) + (= t132 (if t130 t131 t129)) + (= t133 (= (extract 42 42 rn) #b1)) + (if t133 (= t134 (bvadd t132 #x1)) true) + (= t135 (if t133 t134 t132)) + (= t136 (= (extract 43 43 rn) #b1)) + (if t136 (= t137 (bvadd t135 #x1)) true) + (= t138 (if t136 t137 t135)) + (= t139 (= (extract 44 44 rn) #b1)) + (if t139 (= t140 (bvadd t138 #x1)) true) + (= t141 (if t139 t140 t138)) + (= t142 (= (extract 45 45 rn) #b1)) + (if t142 (= t143 (bvadd t141 #x1)) true) + (= t144 (if t142 t143 t141)) + (= t145 (= (extract 46 46 rn) #b1)) + (if t145 (= t146 (bvadd t144 #x1)) true) + (= t147 (if t145 t146 t144)) + (= t148 (= (extract 47 47 rn) #b1)) + (if t148 (= t149 (bvadd t147 #x1)) true) + (= t150 (if t148 t149 t147)) + (= t151 #x0) + (= t152 (= (extract 48 48 rn) #b1)) + (if t152 (= t153 #x1) true) + (= t154 (if t152 t153 t151)) + (= t155 (= (extract 49 49 rn) #b1)) + (if t155 (= t156 (bvadd t154 #x1)) true) + (= t157 (if t155 t156 t154)) + (= t158 (= (extract 50 50 rn) #b1)) + (if t158 (= t159 (bvadd t157 #x1)) true) + (= t160 (if t158 t159 t157)) + (= t161 (= (extract 51 51 rn) #b1)) + (if t161 (= t162 (bvadd t160 #x1)) true) + (= t163 (if t161 t162 t160)) + (= t164 (= (extract 52 52 rn) #b1)) + (if t164 (= t165 (bvadd t163 #x1)) true) + (= t166 (if t164 t165 t163)) + (= t167 (= (extract 53 53 rn) #b1)) + (if t167 (= t168 (bvadd t166 #x1)) true) + (= t169 (if t167 t168 t166)) + (= t170 (= (extract 54 54 rn) #b1)) + (if t170 (= t171 (bvadd t169 #x1)) true) + (= t172 (if t170 t171 t169)) + (= t173 (= (extract 55 55 rn) #b1)) + (if t173 (= t174 (bvadd t172 #x1)) true) + (= t175 (if t173 t174 t172)) + (= t176 #x0) + (= t177 (= (extract 56 56 rn) #b1)) + (if t177 (= t178 #x1) true) + (= t179 (if t177 t178 t176)) + (= t180 (= (extract 57 57 rn) #b1)) + (if t180 (= t181 (bvadd t179 #x1)) true) + (= t182 (if t180 t181 t179)) + (= t183 (= (extract 58 58 rn) #b1)) + (if t183 (= t184 (bvadd t182 #x1)) true) + (= t185 (if t183 t184 t182)) + (= t186 (= (extract 59 59 rn) #b1)) + (if t186 (= t187 (bvadd t185 #x1)) true) + (= t188 (if t186 t187 t185)) + (= t189 (= (extract 60 60 rn) #b1)) + (if t189 (= t190 (bvadd t188 #x1)) true) + (= t191 (if t189 t190 t188)) + (= t192 (= (extract 61 61 rn) #b1)) + (if t192 (= t193 (bvadd t191 #x1)) true) + (= t194 (if t192 t193 t191)) + (= t195 (= (extract 62 62 rn) #b1)) + (if t195 (= t196 (bvadd t194 #x1)) true) + (= t197 (if t195 t196 t194)) + (= t198 (= (extract 63 63 rn) #b1)) + (if t198 (= t199 (bvadd t197 #x1)) true) + (= t200 (if t198 t199 t197)) + (= t201 #x0) + (= t202 (= (extract 64 64 rn) #b1)) + (if t202 (= t203 #x1) true) + (= t204 (if t202 t203 t201)) + (= t205 (= (extract 65 65 rn) #b1)) + (if t205 (= t206 (bvadd t204 #x1)) true) + (= t207 (if t205 t206 t204)) + (= t208 (= (extract 66 66 rn) #b1)) + (if t208 (= t209 (bvadd t207 #x1)) true) + (= t210 (if t208 t209 t207)) + (= t211 (= (extract 67 67 rn) #b1)) + (if t211 (= t212 (bvadd t210 #x1)) true) + (= t213 (if t211 t212 t210)) + (= t214 (= (extract 68 68 rn) #b1)) + (if t214 (= t215 (bvadd t213 #x1)) true) + (= t216 (if t214 t215 t213)) + (= t217 (= (extract 69 69 rn) #b1)) + (if t217 (= t218 (bvadd t216 #x1)) true) + (= t219 (if t217 t218 t216)) + (= t220 (= (extract 70 70 rn) #b1)) + (if t220 (= t221 (bvadd t219 #x1)) true) + (= t222 (if t220 t221 t219)) + (= t223 (= (extract 71 71 rn) #b1)) + (if t223 (= t224 (bvadd t222 #x1)) true) + (= t225 (if t223 t224 t222)) + (= t226 #x0) + (= t227 (= (extract 72 72 rn) #b1)) + (if t227 (= t228 #x1) true) + (= t229 (if t227 t228 t226)) + (= t230 (= (extract 73 73 rn) #b1)) + (if t230 (= t231 (bvadd t229 #x1)) true) + (= t232 (if t230 t231 t229)) + (= t233 (= (extract 74 74 rn) #b1)) + (if t233 (= t234 (bvadd t232 #x1)) true) + (= t235 (if t233 t234 t232)) + (= t236 (= (extract 75 75 rn) #b1)) + (if t236 (= t237 (bvadd t235 #x1)) true) + (= t238 (if t236 t237 t235)) + (= t239 (= (extract 76 76 rn) #b1)) + (if t239 (= t240 (bvadd t238 #x1)) true) + (= t241 (if t239 t240 t238)) + (= t242 (= (extract 77 77 rn) #b1)) + (if t242 (= t243 (bvadd t241 #x1)) true) + (= t244 (if t242 t243 t241)) + (= t245 (= (extract 78 78 rn) #b1)) + (if t245 (= t246 (bvadd t244 #x1)) true) + (= t247 (if t245 t246 t244)) + (= t248 (= (extract 79 79 rn) #b1)) + (if t248 (= t249 (bvadd t247 #x1)) true) + (= t250 (if t248 t249 t247)) + (= t251 #x0) + (= t252 (= (extract 80 80 rn) #b1)) + (if t252 (= t253 #x1) true) + (= t254 (if t252 t253 t251)) + (= t255 (= (extract 81 81 rn) #b1)) + (if t255 (= t256 (bvadd t254 #x1)) true) + (= t257 (if t255 t256 t254)) + (= t258 (= (extract 82 82 rn) #b1)) + (if t258 (= t259 (bvadd t257 #x1)) true) + (= t260 (if t258 t259 t257)) + (= t261 (= (extract 83 83 rn) #b1)) + (if t261 (= t262 (bvadd t260 #x1)) true) + (= t263 (if t261 t262 t260)) + (= t264 (= (extract 84 84 rn) #b1)) + (if t264 (= t265 (bvadd t263 #x1)) true) + (= t266 (if t264 t265 t263)) + (= t267 (= (extract 85 85 rn) #b1)) + (if t267 (= t268 (bvadd t266 #x1)) true) + (= t269 (if t267 t268 t266)) + (= t270 (= (extract 86 86 rn) #b1)) + (if t270 (= t271 (bvadd t269 #x1)) true) + (= t272 (if t270 t271 t269)) + (= t273 (= (extract 87 87 rn) #b1)) + (if t273 (= t274 (bvadd t272 #x1)) true) + (= t275 (if t273 t274 t272)) + (= t276 #x0) + (= t277 (= (extract 88 88 rn) #b1)) + (if t277 (= t278 #x1) true) + (= t279 (if t277 t278 t276)) + (= t280 (= (extract 89 89 rn) #b1)) + (if t280 (= t281 (bvadd t279 #x1)) true) + (= t282 (if t280 t281 t279)) + (= t283 (= (extract 90 90 rn) #b1)) + (if t283 (= t284 (bvadd t282 #x1)) true) + (= t285 (if t283 t284 t282)) + (= t286 (= (extract 91 91 rn) #b1)) + (if t286 (= t287 (bvadd t285 #x1)) true) + (= t288 (if t286 t287 t285)) + (= t289 (= (extract 92 92 rn) #b1)) + (if t289 (= t290 (bvadd t288 #x1)) true) + (= t291 (if t289 t290 t288)) + (= t292 (= (extract 93 93 rn) #b1)) + (if t292 (= t293 (bvadd t291 #x1)) true) + (= t294 (if t292 t293 t291)) + (= t295 (= (extract 94 94 rn) #b1)) + (if t295 (= t296 (bvadd t294 #x1)) true) + (= t297 (if t295 t296 t294)) + (= t298 (= (extract 95 95 rn) #b1)) + (if t298 (= t299 (bvadd t297 #x1)) true) + (= t300 (if t298 t299 t297)) + (= t301 #x0) + (= t302 (= (extract 96 96 rn) #b1)) + (if t302 (= t303 #x1) true) + (= t304 (if t302 t303 t301)) + (= t305 (= (extract 97 97 rn) #b1)) + (if t305 (= t306 (bvadd t304 #x1)) true) + (= t307 (if t305 t306 t304)) + (= t308 (= (extract 98 98 rn) #b1)) + (if t308 (= t309 (bvadd t307 #x1)) true) + (= t310 (if t308 t309 t307)) + (= t311 (= (extract 99 99 rn) #b1)) + (if t311 (= t312 (bvadd t310 #x1)) true) + (= t313 (if t311 t312 t310)) + (= t314 (= (extract 100 100 rn) #b1)) + (if t314 (= t315 (bvadd t313 #x1)) true) + (= t316 (if t314 t315 t313)) + (= t317 (= (extract 101 101 rn) #b1)) + (if t317 (= t318 (bvadd t316 #x1)) true) + (= t319 (if t317 t318 t316)) + (= t320 (= (extract 102 102 rn) #b1)) + (if t320 (= t321 (bvadd t319 #x1)) true) + (= t322 (if t320 t321 t319)) + (= t323 (= (extract 103 103 rn) #b1)) + (if t323 (= t324 (bvadd t322 #x1)) true) + (= t325 (if t323 t324 t322)) + (= t326 #x0) + (= t327 (= (extract 104 104 rn) #b1)) + (if t327 (= t328 #x1) true) + (= t329 (if t327 t328 t326)) + (= t330 (= (extract 105 105 rn) #b1)) + (if t330 (= t331 (bvadd t329 #x1)) true) + (= t332 (if t330 t331 t329)) + (= t333 (= (extract 106 106 rn) #b1)) + (if t333 (= t334 (bvadd t332 #x1)) true) + (= t335 (if t333 t334 t332)) + (= t336 (= (extract 107 107 rn) #b1)) + (if t336 (= t337 (bvadd t335 #x1)) true) + (= t338 (if t336 t337 t335)) + (= t339 (= (extract 108 108 rn) #b1)) + (if t339 (= t340 (bvadd t338 #x1)) true) + (= t341 (if t339 t340 t338)) + (= t342 (= (extract 109 109 rn) #b1)) + (if t342 (= t343 (bvadd t341 #x1)) true) + (= t344 (if t342 t343 t341)) + (= t345 (= (extract 110 110 rn) #b1)) + (if t345 (= t346 (bvadd t344 #x1)) true) + (= t347 (if t345 t346 t344)) + (= t348 (= (extract 111 111 rn) #b1)) + (if t348 (= t349 (bvadd t347 #x1)) true) + (= t350 (if t348 t349 t347)) + (= t351 #x0) + (= t352 (= (extract 112 112 rn) #b1)) + (if t352 (= t353 #x1) true) + (= t354 (if t352 t353 t351)) + (= t355 (= (extract 113 113 rn) #b1)) + (if t355 (= t356 (bvadd t354 #x1)) true) + (= t357 (if t355 t356 t354)) + (= t358 (= (extract 114 114 rn) #b1)) + (if t358 (= t359 (bvadd t357 #x1)) true) + (= t360 (if t358 t359 t357)) + (= t361 (= (extract 115 115 rn) #b1)) + (if t361 (= t362 (bvadd t360 #x1)) true) + (= t363 (if t361 t362 t360)) + (= t364 (= (extract 116 116 rn) #b1)) + (if t364 (= t365 (bvadd t363 #x1)) true) + (= t366 (if t364 t365 t363)) + (= t367 (= (extract 117 117 rn) #b1)) + (if t367 (= t368 (bvadd t366 #x1)) true) + (= t369 (if t367 t368 t366)) + (= t370 (= (extract 118 118 rn) #b1)) + (if t370 (= t371 (bvadd t369 #x1)) true) + (= t372 (if t370 t371 t369)) + (= t373 (= (extract 119 119 rn) #b1)) + (if t373 (= t374 (bvadd t372 #x1)) true) + (= t375 (if t373 t374 t372)) + (= t376 #x0) + (= t377 (= (extract 120 120 rn) #b1)) + (if t377 (= t378 #x1) true) + (= t379 (if t377 t378 t376)) + (= t380 (= (extract 121 121 rn) #b1)) + (if t380 (= t381 (bvadd t379 #x1)) true) + (= t382 (if t380 t381 t379)) + (= t383 (= (extract 122 122 rn) #b1)) + (if t383 (= t384 (bvadd t382 #x1)) true) + (= t385 (if t383 t384 t382)) + (= t386 (= (extract 123 123 rn) #b1)) + (if t386 (= t387 (bvadd t385 #x1)) true) + (= t388 (if t386 t387 t385)) + (= t389 (= (extract 124 124 rn) #b1)) + (if t389 (= t390 (bvadd t388 #x1)) true) + (= t391 (if t389 t390 t388)) + (= t392 (= (extract 125 125 rn) #b1)) + (if t392 (= t393 (bvadd t391 #x1)) true) + (= t394 (if t392 t393 t391)) + (= t395 (= (extract 126 126 rn) #b1)) + (if t395 (= t396 (bvadd t394 #x1)) true) + (= t397 (if t395 t396 t394)) + (= t398 (= (extract 127 127 rn) #b1)) + (if t398 (= t399 (bvadd t397 #x1)) true) + (= t400 (if t398 t399 t397)) + (= + rd + (concat + (zero_ext 8 t400) + (concat + (zero_ext 8 t375) + (concat + (zero_ext 8 t350) + (concat + (zero_ext 8 t325) + (concat + (zero_ext 8 t300) + (concat + (zero_ext 8 t275) + (concat + (zero_ext 8 t250) + (concat + (zero_ext 8 t225) + (concat + (zero_ext 8 t200) + (concat + (zero_ext 8 t175) + (concat + (zero_ext 8 t150) + (concat + (zero_ext 8 t125) + (concat (zero_ext 8 t100) (concat (zero_ext 8 t75) (concat (zero_ext 8 t50) (zero_ext 8 t25)))) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ((Size8x8) + (match + op + ((Cnt) + (with + (t0 + t10 + t100 + t101 + t102 + t103 + t104 + t105 + t106 + t107 + t108 + t109 + t11 + t110 + t111 + t112 + t113 + t114 + t115 + t116 + t117 + t118 + t119 + t12 + t120 + t121 + t122 + t123 + t124 + t125 + t126 + t127 + t128 + t129 + t13 + t130 + t131 + t132 + t133 + t134 + t135 + t136 + t137 + t138 + t139 + t14 + t140 + t141 + t142 + t143 + t144 + t145 + t146 + t147 + t148 + t149 + t15 + t150 + t151 + t152 + t153 + t154 + t155 + t156 + t157 + t158 + t159 + t16 + t160 + t161 + t162 + t163 + t164 + t165 + t166 + t167 + t168 + t169 + t17 + t170 + t171 + t172 + t173 + t174 + t175 + t176 + t177 + t178 + t179 + t18 + t180 + t181 + t182 + t183 + t184 + t185 + t186 + t187 + t188 + t189 + t19 + t190 + t191 + t192 + t193 + t194 + t195 + t196 + t197 + t198 + t199 + t2 + t20 + t200 + t21 + t22 + t23 + t24 + t25 + t26 + t27 + t28 + t29 + t3 + t30 + t31 + t32 + t33 + t34 + t35 + t36 + t37 + t38 + t39 + t4 + t40 + t41 + t42 + t43 + t44 + t45 + t46 + t47 + t48 + t49 + t5 + t50 + t51 + t52 + t53 + t54 + t55 + t56 + t57 + t58 + t59 + t6 + t60 + t61 + t62 + t63 + t64 + t65 + t66 + t67 + t68 + t69 + t7 + t70 + t71 + t72 + t73 + t74 + t75 + t76 + t77 + t78 + t79 + t8 + t80 + t81 + t82 + t83 + t84 + t85 + t86 + t87 + t88 + t89 + t9 + t90 + t91 + t92 + t93 + t94 + t95 + t96 + t97 + t98 + t99 + ) + (and + (= t0 #x0) + (= t2 (= (extract 0 0 rn) #b1)) + (if t2 (= t3 #x1) true) + (= t4 (if t2 t3 t0)) + (= t5 (= (extract 1 1 rn) #b1)) + (if t5 (= t6 (bvadd t4 #x1)) true) + (= t7 (if t5 t6 t4)) + (= t8 (= (extract 2 2 rn) #b1)) + (if t8 (= t9 (bvadd t7 #x1)) true) + (= t10 (if t8 t9 t7)) + (= t11 (= (extract 3 3 rn) #b1)) + (if t11 (= t12 (bvadd t10 #x1)) true) + (= t13 (if t11 t12 t10)) + (= t14 (= (extract 4 4 rn) #b1)) + (if t14 (= t15 (bvadd t13 #x1)) true) + (= t16 (if t14 t15 t13)) + (= t17 (= (extract 5 5 rn) #b1)) + (if t17 (= t18 (bvadd t16 #x1)) true) + (= t19 (if t17 t18 t16)) + (= t20 (= (extract 6 6 rn) #b1)) + (if t20 (= t21 (bvadd t19 #x1)) true) + (= t22 (if t20 t21 t19)) + (= t23 (= (extract 7 7 rn) #b1)) + (if t23 (= t24 (bvadd t22 #x1)) true) + (= t25 (if t23 t24 t22)) + (= t26 #x0) + (= t27 (= (extract 8 8 rn) #b1)) + (if t27 (= t28 #x1) true) + (= t29 (if t27 t28 t26)) + (= t30 (= (extract 9 9 rn) #b1)) + (if t30 (= t31 (bvadd t29 #x1)) true) + (= t32 (if t30 t31 t29)) + (= t33 (= (extract 10 10 rn) #b1)) + (if t33 (= t34 (bvadd t32 #x1)) true) + (= t35 (if t33 t34 t32)) + (= t36 (= (extract 11 11 rn) #b1)) + (if t36 (= t37 (bvadd t35 #x1)) true) + (= t38 (if t36 t37 t35)) + (= t39 (= (extract 12 12 rn) #b1)) + (if t39 (= t40 (bvadd t38 #x1)) true) + (= t41 (if t39 t40 t38)) + (= t42 (= (extract 13 13 rn) #b1)) + (if t42 (= t43 (bvadd t41 #x1)) true) + (= t44 (if t42 t43 t41)) + (= t45 (= (extract 14 14 rn) #b1)) + (if t45 (= t46 (bvadd t44 #x1)) true) + (= t47 (if t45 t46 t44)) + (= t48 (= (extract 15 15 rn) #b1)) + (if t48 (= t49 (bvadd t47 #x1)) true) + (= t50 (if t48 t49 t47)) + (= t51 #x0) + (= t52 (= (extract 16 16 rn) #b1)) + (if t52 (= t53 #x1) true) + (= t54 (if t52 t53 t51)) + (= t55 (= (extract 17 17 rn) #b1)) + (if t55 (= t56 (bvadd t54 #x1)) true) + (= t57 (if t55 t56 t54)) + (= t58 (= (extract 18 18 rn) #b1)) + (if t58 (= t59 (bvadd t57 #x1)) true) + (= t60 (if t58 t59 t57)) + (= t61 (= (extract 19 19 rn) #b1)) + (if t61 (= t62 (bvadd t60 #x1)) true) + (= t63 (if t61 t62 t60)) + (= t64 (= (extract 20 20 rn) #b1)) + (if t64 (= t65 (bvadd t63 #x1)) true) + (= t66 (if t64 t65 t63)) + (= t67 (= (extract 21 21 rn) #b1)) + (if t67 (= t68 (bvadd t66 #x1)) true) + (= t69 (if t67 t68 t66)) + (= t70 (= (extract 22 22 rn) #b1)) + (if t70 (= t71 (bvadd t69 #x1)) true) + (= t72 (if t70 t71 t69)) + (= t73 (= (extract 23 23 rn) #b1)) + (if t73 (= t74 (bvadd t72 #x1)) true) + (= t75 (if t73 t74 t72)) + (= t76 #x0) + (= t77 (= (extract 24 24 rn) #b1)) + (if t77 (= t78 #x1) true) + (= t79 (if t77 t78 t76)) + (= t80 (= (extract 25 25 rn) #b1)) + (if t80 (= t81 (bvadd t79 #x1)) true) + (= t82 (if t80 t81 t79)) + (= t83 (= (extract 26 26 rn) #b1)) + (if t83 (= t84 (bvadd t82 #x1)) true) + (= t85 (if t83 t84 t82)) + (= t86 (= (extract 27 27 rn) #b1)) + (if t86 (= t87 (bvadd t85 #x1)) true) + (= t88 (if t86 t87 t85)) + (= t89 (= (extract 28 28 rn) #b1)) + (if t89 (= t90 (bvadd t88 #x1)) true) + (= t91 (if t89 t90 t88)) + (= t92 (= (extract 29 29 rn) #b1)) + (if t92 (= t93 (bvadd t91 #x1)) true) + (= t94 (if t92 t93 t91)) + (= t95 (= (extract 30 30 rn) #b1)) + (if t95 (= t96 (bvadd t94 #x1)) true) + (= t97 (if t95 t96 t94)) + (= t98 (= (extract 31 31 rn) #b1)) + (if t98 (= t99 (bvadd t97 #x1)) true) + (= t100 (if t98 t99 t97)) + (= t101 #x0) + (= t102 (= (extract 32 32 rn) #b1)) + (if t102 (= t103 #x1) true) + (= t104 (if t102 t103 t101)) + (= t105 (= (extract 33 33 rn) #b1)) + (if t105 (= t106 (bvadd t104 #x1)) true) + (= t107 (if t105 t106 t104)) + (= t108 (= (extract 34 34 rn) #b1)) + (if t108 (= t109 (bvadd t107 #x1)) true) + (= t110 (if t108 t109 t107)) + (= t111 (= (extract 35 35 rn) #b1)) + (if t111 (= t112 (bvadd t110 #x1)) true) + (= t113 (if t111 t112 t110)) + (= t114 (= (extract 36 36 rn) #b1)) + (if t114 (= t115 (bvadd t113 #x1)) true) + (= t116 (if t114 t115 t113)) + (= t117 (= (extract 37 37 rn) #b1)) + (if t117 (= t118 (bvadd t116 #x1)) true) + (= t119 (if t117 t118 t116)) + (= t120 (= (extract 38 38 rn) #b1)) + (if t120 (= t121 (bvadd t119 #x1)) true) + (= t122 (if t120 t121 t119)) + (= t123 (= (extract 39 39 rn) #b1)) + (if t123 (= t124 (bvadd t122 #x1)) true) + (= t125 (if t123 t124 t122)) + (= t126 #x0) + (= t127 (= (extract 40 40 rn) #b1)) + (if t127 (= t128 #x1) true) + (= t129 (if t127 t128 t126)) + (= t130 (= (extract 41 41 rn) #b1)) + (if t130 (= t131 (bvadd t129 #x1)) true) + (= t132 (if t130 t131 t129)) + (= t133 (= (extract 42 42 rn) #b1)) + (if t133 (= t134 (bvadd t132 #x1)) true) + (= t135 (if t133 t134 t132)) + (= t136 (= (extract 43 43 rn) #b1)) + (if t136 (= t137 (bvadd t135 #x1)) true) + (= t138 (if t136 t137 t135)) + (= t139 (= (extract 44 44 rn) #b1)) + (if t139 (= t140 (bvadd t138 #x1)) true) + (= t141 (if t139 t140 t138)) + (= t142 (= (extract 45 45 rn) #b1)) + (if t142 (= t143 (bvadd t141 #x1)) true) + (= t144 (if t142 t143 t141)) + (= t145 (= (extract 46 46 rn) #b1)) + (if t145 (= t146 (bvadd t144 #x1)) true) + (= t147 (if t145 t146 t144)) + (= t148 (= (extract 47 47 rn) #b1)) + (if t148 (= t149 (bvadd t147 #x1)) true) + (= t150 (if t148 t149 t147)) + (= t151 #x0) + (= t152 (= (extract 48 48 rn) #b1)) + (if t152 (= t153 #x1) true) + (= t154 (if t152 t153 t151)) + (= t155 (= (extract 49 49 rn) #b1)) + (if t155 (= t156 (bvadd t154 #x1)) true) + (= t157 (if t155 t156 t154)) + (= t158 (= (extract 50 50 rn) #b1)) + (if t158 (= t159 (bvadd t157 #x1)) true) + (= t160 (if t158 t159 t157)) + (= t161 (= (extract 51 51 rn) #b1)) + (if t161 (= t162 (bvadd t160 #x1)) true) + (= t163 (if t161 t162 t160)) + (= t164 (= (extract 52 52 rn) #b1)) + (if t164 (= t165 (bvadd t163 #x1)) true) + (= t166 (if t164 t165 t163)) + (= t167 (= (extract 53 53 rn) #b1)) + (if t167 (= t168 (bvadd t166 #x1)) true) + (= t169 (if t167 t168 t166)) + (= t170 (= (extract 54 54 rn) #b1)) + (if t170 (= t171 (bvadd t169 #x1)) true) + (= t172 (if t170 t171 t169)) + (= t173 (= (extract 55 55 rn) #b1)) + (if t173 (= t174 (bvadd t172 #x1)) true) + (= t175 (if t173 t174 t172)) + (= t176 #x0) + (= t177 (= (extract 56 56 rn) #b1)) + (if t177 (= t178 #x1) true) + (= t179 (if t177 t178 t176)) + (= t180 (= (extract 57 57 rn) #b1)) + (if t180 (= t181 (bvadd t179 #x1)) true) + (= t182 (if t180 t181 t179)) + (= t183 (= (extract 58 58 rn) #b1)) + (if t183 (= t184 (bvadd t182 #x1)) true) + (= t185 (if t183 t184 t182)) + (= t186 (= (extract 59 59 rn) #b1)) + (if t186 (= t187 (bvadd t185 #x1)) true) + (= t188 (if t186 t187 t185)) + (= t189 (= (extract 60 60 rn) #b1)) + (if t189 (= t190 (bvadd t188 #x1)) true) + (= t191 (if t189 t190 t188)) + (= t192 (= (extract 61 61 rn) #b1)) + (if t192 (= t193 (bvadd t191 #x1)) true) + (= t194 (if t192 t193 t191)) + (= t195 (= (extract 62 62 rn) #b1)) + (if t195 (= t196 (bvadd t194 #x1)) true) + (= t197 (if t195 t196 t194)) + (= t198 (= (extract 63 63 rn) #b1)) + (if t198 (= t199 (bvadd t197 #x1)) true) + (= t200 (if t198 t199 t197)) + (= + rd + (zero_ext + 128 + (concat + (zero_ext 8 t200) + (concat + (zero_ext 8 t175) + (concat + (zero_ext 8 t150) + (concat + (zero_ext 8 t125) + (concat (zero_ext 8 t100) (concat (zero_ext 8 t75) (concat (zero_ext 8 t50) (zero_ext 8 t25)))) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (require (match size ((Size8x16) (match op ((Cnt) true))) ((Size8x8) (match op ((Cnt) true))))) +) diff --git a/cranelift/codegen/src/isa/aarch64/spec/vec_rrr.isle b/cranelift/codegen/src/isa/aarch64/spec/vec_rrr.isle new file mode 100644 index 000000000000..91110ea3d418 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/spec/vec_rrr.isle @@ -0,0 +1,98 @@ +;; GENERATED BY `isaspec`. DO NOT EDIT!!! + +(attr MInst.VecRRR (tag isaspec_generated)) + +(spec + (MInst.VecRRR alu_op rd rn rm size) + (provide + (match + size + ((Size8x16) + (match + alu_op + ((Addp) + (= + rd + (concat + (bvadd (extract 119 112 rm) (extract 127 120 rm)) + (concat + (bvadd (extract 103 96 rm) (extract 111 104 rm)) + (concat + (bvadd (extract 87 80 rm) (extract 95 88 rm)) + (concat + (bvadd (extract 71 64 rm) (extract 79 72 rm)) + (concat + (bvadd (extract 55 48 rm) (extract 63 56 rm)) + (concat + (bvadd (extract 39 32 rm) (extract 47 40 rm)) + (concat + (bvadd (extract 23 16 rm) (extract 31 24 rm)) + (concat + (bvadd (extract 7 0 rm) (extract 15 8 rm)) + (concat + (bvadd (extract 119 112 rn) (extract 127 120 rn)) + (concat + (bvadd (extract 103 96 rn) (extract 111 104 rn)) + (concat + (bvadd (extract 87 80 rn) (extract 95 88 rn)) + (concat + (bvadd (extract 71 64 rn) (extract 79 72 rn)) + (concat + (bvadd (extract 55 48 rn) (extract 63 56 rn)) + (concat + (bvadd (extract 39 32 rn) (extract 47 40 rn)) + (concat (bvadd (extract 23 16 rn) (extract 31 24 rn)) (bvadd (extract 7 0 rn) (extract 15 8 rn))) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ((Size8x8) + (match + alu_op + ((Addp) + (= + rd + (zero_ext + 128 + (concat + (bvadd (extract 55 48 rm) (extract 63 56 rm)) + (concat + (bvadd (extract 39 32 rm) (extract 47 40 rm)) + (concat + (bvadd (extract 23 16 rm) (extract 31 24 rm)) + (concat + (bvadd (extract 7 0 rm) (extract 15 8 rm)) + (concat + (bvadd (extract 55 48 rn) (extract 63 56 rn)) + (concat + (bvadd (extract 39 32 rn) (extract 47 40 rn)) + (concat (bvadd (extract 23 16 rn) (extract 31 24 rn)) (bvadd (extract 7 0 rn) (extract 15 8 rn))) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (require (match size ((Size8x16) (match alu_op ((Addp) true))) ((Size8x8) (match alu_op ((Addp) true))))) +) diff --git a/cranelift/codegen/src/isa/pulley_shared/lower.isle b/cranelift/codegen/src/isa/pulley_shared/lower.isle index b907eb9d3509..85b2cb0985c6 100644 --- a/cranelift/codegen/src/isa/pulley_shared/lower.isle +++ b/cranelift/codegen/src/isa/pulley_shared/lower.isle @@ -1919,7 +1919,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ block (u64_from_imm64 idx))) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (pulley_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 80d812aaf249..5c542849f27d 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -3038,8 +3038,8 @@ (rule 7 (gen_bitcast r (ty_supported_float_min src_ty) (ty_supported_vec _)) (rv_vfmv_sf r src_ty)) (rule 6 (gen_bitcast r (ty_supported_vec _) (ty_supported_float_min dst_ty)) (rv_vfmv_fs r dst_ty)) -(rule 5 (gen_bitcast r (ty_int_ref_scalar_64 src_ty) (ty_supported_vec _)) (rv_vmv_sx r src_ty)) -(rule 4 (gen_bitcast r (ty_supported_vec _) (ty_int_ref_scalar_64 dst_ty)) (rv_vmv_xs r dst_ty)) +(rule 5 (gen_bitcast r (ty_int_ref_scalar_64_extract src_ty) (ty_supported_vec _)) (rv_vmv_sx r src_ty)) +(rule 4 (gen_bitcast r (ty_supported_vec _) (ty_int_ref_scalar_64_extract dst_ty)) (rv_vmv_xs r dst_ty)) (rule 3 (gen_bitcast r (ty_supported_float_min $F16) $I16) (rv_fmvxh r)) (rule 2 (gen_bitcast r (ty_supported_float_size $F16) $I16) (rv_fmvxw r)) (rule 2 (gen_bitcast r (ty_supported_float_size $F32) $I32) (rv_fmvxw r)) @@ -3053,7 +3053,7 @@ (rule 0 (gen_bitcast r $I32 (ty_supported_float_size $F32)) (rv_fmvwx r)) (rule 0 (gen_bitcast r $I64 (ty_supported_float_size $F64)) (rv_fmvdx r)) (rule -1 (gen_bitcast r (ty_supported_float_size _) (ty_supported_float_size _)) r) -(rule -2 (gen_bitcast r (ty_int_ref_scalar_64 _) (ty_int_ref_scalar_64 _)) r) +(rule -2 (gen_bitcast r (ty_int_ref_scalar_64_extract _) (ty_int_ref_scalar_64_extract _)) r) (rule -3 (gen_bitcast r (ty_supported_vec _) (ty_supported_vec _)) r) (decl move_f_to_x (FReg Type) XReg) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index a074c9f903df..5a6e0e7d4854 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -48,10 +48,10 @@ (rv_add x y)) ;; Special cases for when one operand is an immediate that fits in 12 bits. -(rule 1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (imm12_from_value y)))) +(rule 1 (lower (has_type (ty_int_ref_scalar_64_extract ty) (iadd _ x (imm12_from_value y)))) (alu_rr_imm12 (select_addi ty) x y)) -(rule 2 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (imm12_from_value x) y))) +(rule 2 (lower (has_type (ty_int_ref_scalar_64_extract ty) (iadd _ (imm12_from_value x) y))) (alu_rr_imm12 (select_addi ty) y x)) ;; Special case when one of the operands is uextended @@ -365,7 +365,7 @@ (sub_i128 x y)) ;; Switch to an `addi` by a negative if we can fit the value in an `imm12`. -(rule 3 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x y))) +(rule 3 (lower (has_type (ty_int_ref_scalar_64_extract ty) (isub _ x y))) (if-let imm12_neg (imm12_from_negated_value y)) (alu_rr_imm12 (select_addi ty) x imm12_neg)) @@ -488,7 +488,7 @@ ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (imul _ x y))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64_extract ty) (imul _ x y))) (rv_mul x y)) (rule 1 (lower (has_type (fits_in_32 (ty_int ty)) (imul _ x y))) @@ -545,7 +545,7 @@ (rv_vmul_vx x y (unmasked) ty)) ;;;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (smulhi _ x y))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64_extract ty) (smulhi _ x y))) (lower_smlhi ty (sext x) (sext y))) (rule 1 (lower (has_type (ty_supported_vec ty) (smulhi _ x y))) @@ -1013,7 +1013,7 @@ ;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 _) (bnot _ x))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64_extract _) (bnot _ x))) (rv_not x)) (rule 1 (lower (has_type (ty_supported_float_size ty) (bnot _ x))) @@ -1027,13 +1027,13 @@ (rule 3 (lower (has_type (ty_supported_vec ty) (bnot _ x))) (rv_vnot_v x (unmasked) ty)) -(rule 4 (lower (has_type (ty_int_ref_scalar_64 _) (bnot _ (bxor _ x y)))) +(rule 4 (lower (has_type (ty_int_ref_scalar_64_extract _) (bnot _ (bxor _ x y)))) (if-let true (has_zbb)) (rv_xnor x y)) ;;;; Rules for `bit_reverse` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (bitrev _ x))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64_extract ty) (bitrev _ x))) (gen_bitrev ty x)) (rule 1 (lower (has_type $I128 (bitrev _ x))) @@ -1926,13 +1926,13 @@ ;; of the iconst rule because that runs into regalloc issues. gen_select_xreg ;; has some optimizations based on the use of the zero register so we have to ;; manually match it here. -(rule 5 (lower (has_type (ty_int_ref_scalar_64 _) (select _ c (i64_from_iconst 0) y))) +(rule 5 (lower (has_type (ty_int_ref_scalar_64_extract _) (select _ c (i64_from_iconst 0) y))) (gen_select_xreg (is_nonzero_cmp c) (zero_reg) y)) -(rule 4 (lower (has_type (ty_int_ref_scalar_64 _) (select _ c x (i64_from_iconst 0)))) +(rule 4 (lower (has_type (ty_int_ref_scalar_64_extract _) (select _ c x (i64_from_iconst 0)))) (gen_select_xreg (is_nonzero_cmp c) x (zero_reg))) -(rule 3 (lower (has_type (ty_int_ref_scalar_64 _) (select _ c x y))) +(rule 3 (lower (has_type (ty_int_ref_scalar_64_extract _) (select _ c x y))) (gen_select_xreg (is_nonzero_cmp c) x y)) (rule 2 (lower (has_type (ty_reg_pair _) (select _ c x y))) @@ -1947,7 +1947,7 @@ ;;;;; Rules for `bitselect`;;;;;;;;; ;; Do a (c & x) | (~c & y) operation. -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (bitselect _ c x y))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64_extract ty) (bitselect _ c x y))) (let ((tmp_x XReg (rv_and c x)) (c_inverse XReg (rv_not c)) (tmp_y XReg (rv_and c_inverse y))) @@ -2673,7 +2673,7 @@ ;; sext.{b,h,w} a0, a0 ;; neg a1, a0 ;; max a0, a0, a1 -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iabs _ x))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64_extract ty) (iabs _ x))) (let ((extended XReg (sext x)) (negated XReg (rv_neg extended))) (gen_select_xreg (cmp_gt extended negated) extended negated))) @@ -2812,7 +2812,7 @@ (rule 0 (lower (has_type ty (splat _ n @ (value_type (ty_supported_float_full _))))) (rv_vfmv_vf n ty)) -(rule 1 (lower (has_type ty (splat _ n @ (value_type (ty_int_ref_scalar_64 _))))) +(rule 1 (lower (has_type ty (splat _ n @ (value_type (ty_int_ref_scalar_64_extract _))))) (rv_vmv_vx n ty)) (rule 2 (lower (has_type ty (splat _ (iconst _ (u64_from_imm64 (imm5_from_u64 imm)))))) @@ -3123,7 +3123,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ block (u64_from_imm64 idx))) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (rv64_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/s390x/lower.isle b/cranelift/codegen/src/isa/s390x/lower.isle index 560ed86ddca9..7e0b5987e097 100644 --- a/cranelift/codegen/src/isa/s390x/lower.isle +++ b/cranelift/codegen/src/isa/s390x/lower.isle @@ -4673,7 +4673,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ block (u64_from_imm64 idx))) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (s390x_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle index c28842c10618..a4688e365cea 100644 --- a/cranelift/codegen/src/isa/x64/inst.isle +++ b/cranelift/codegen/src/isa/x64/inst.isle @@ -4,7 +4,7 @@ ;; Don't build `MInst` variants directly, in general. Instead, use the ;; instruction-emitting helpers defined further down. - +(model MInst (type (bv 1))) (type MInst nodebug (enum ;; ========================================= @@ -319,6 +319,122 @@ ;; An instruction assembled outside of cranelift-codegen. (External (inst AssemblerInst)))) +(spec + (MInst.MovzxRmR ext_mode src dst) + (modifies isa_load) + + (provide + (=> (not (:isMem src)) + (= dst (:data src))) + ;; Only handle the “memory” case; require that isMem=1 + ;; TODO: handle more cases https://github.com/wellesley-prog-sys/wasmtime/issues/31 + (=> + ( :isMem src ) + (and ;; isMem true + ;; mark that we did a load + (= (:active isa_load) true) + ;; record how many bits we loaded + (= (:size_bits isa_load) + (switch ext_mode + ((ExtMode.BL) 8) ((ExtMode.BQ) 8) + ((ExtMode.WL) 16) ((ExtMode.WQ) 16) + ((ExtMode.LQ) 32))) + ;; compute effective address from the raw 64‑bit data field + (= (:addr isa_load) (:data src)) + ;; zero‑extend the loaded value into dst + (= dst + (switch ext_mode + ((ExtMode.BL) (zero_ext 64 (conv_to 8 loaded_value))) + ((ExtMode.BQ) (zero_ext 64 (conv_to 8 loaded_value))) + ((ExtMode.WL) (zero_ext 64 (conv_to 16 loaded_value))) + ((ExtMode.WQ) (zero_ext 64 (conv_to 16 loaded_value))) + ((ExtMode.LQ) (zero_ext 64 (conv_to 32 loaded_value))))))))) + +(spec (MInst.MovRM size src dst) + ;; marks store active + (modifies isa_store) + ;; provide block + (provide + ;; we are doing a store + (= (:active isa_store) true) + ;; size in bit = size * 8 + (= (:size_bits isa_store) + (switch size + ((OperandSize.Size8) 8) + ((OperandSize.Size16) 16) + ((OperandSize.Size32) 32) + ((OperandSize.Size64) 64) + ) + ) + ;; address + (= (:addr isa_store) dst) + ;; value to store + (= (:value isa_store) src))) + +(spec (MInst.AluRM size op src1 src2) + ;; active load and store + (modifies isa_load) + (modifies isa_store) + ;; provide block + (provide + ;; load from memory + (= (:active isa_load) true) + (= (:addr isa_load) src1) + (= (:size_bits isa_load) + (switch size + ((OperandSize.Size32) 32) + ((OperandSize.Size8) 8) + ((OperandSize.Size16) 16) + ((OperandSize.Size64) 64))) + ;; compute new value + (= (:value isa_store) + (bvxor loaded_value src2 + )) + ;; store back to memory + (= (:active isa_store) true) + (= (:addr isa_store) src1) + (= (:size_bits isa_store) (:size_bits isa_load)))) + +(spec (MInst.AluRmiR size op src1 src2 dst) + (provide + ; Execute opcode. + (= + dst + (switch size + ; 64-bit forms operate on full register + ((OperandSize.Size64) + (switch op + ((AluRmiROpcode.Add) (bvadd src1 src2)) + ) + ) + ; 32-bit forms zero-extend to full general purpose register. + ((OperandSize.Size32) + (switch op + ((AluRmiROpcode.Add) (zero_ext 64 (bvadd (extract 31 0 src1) (extract 31 0 src2)))) + ) + ) + ) + ) + ) +) + +(spec + (MInst.LoadEffectiveAddress + addr ; SyntheticAmode + dst ; WritableGpr + size ; OperandSize + ) + (provide + (= + dst + (switch size + ((OperandSize.Size64) addr) + ((OperandSize.Size32) (zero_ext 64 (extract 31 0 addr))) + ) + ) + ) +) + (type AssemblerInst extern (enum)) (type OperandSize extern @@ -335,10 +451,21 @@ (type BoxAtomic128XchgSeqArgs extern (enum)) ;; Get the `OperandSize` for a given `Type`, rounding smaller types up to 32 bits. +(spec (operand_size_of_type_32_64 ty) + (provide + (= result (if (= (:bits ty) 64) (OperandSize.Size64) (OperandSize.Size32))))) (decl operand_size_of_type_32_64 (Type) OperandSize) (extern constructor operand_size_of_type_32_64 operand_size_of_type_32_64) ;; Get the true `OperandSize` for a given `Type`, with no rounding. +(spec (raw_operand_size_of_type ty) + (provide + (= result + (switch (:bits ty) + (8 (OperandSize.Size8)) + (16 (OperandSize.Size16)) + (32 (OperandSize.Size32)) + (64 (OperandSize.Size64)))))) (decl raw_operand_size_of_type (Type) OperandSize) (extern constructor raw_operand_size_of_type raw_operand_size_of_type) @@ -359,21 +486,38 @@ (rule (operand_size_bits (OperandSize.Size32)) 32) (rule (operand_size_bits (OperandSize.Size64)) 64) +(model RegMemImm (type (bv))) (type RegMemImm extern (enum (Reg (reg Reg)) (Mem (addr SyntheticAmode)) (Imm (simm32 u32)))) +(spec (RegMemImm.Imm simm32) (provide + (= result + (bvor (bvshl #b10 62)(zero_ext 64 simm32))) +)) + +(spec (RegMemImm.Mem addr) (provide + (= result + (bvor + #x4000000000000000 addr + )) +)) + ;; Put the given clif value into a `RegMemImm` operand. ;; ;; Asserts that the value fits into a single register, and doesn't require ;; multiple registers for its representation (like `i128` for example). ;; ;; As a side effect, this marks the value as used. +(spec (put_in_reg_mem_imm arg) + (provide (= result (conv_to (widthof result) arg))) +) (decl put_in_reg_mem_imm (Value) RegMemImm) (extern constructor put_in_reg_mem_imm put_in_reg_mem_imm) +(model RegMem (type (bv))) (type RegMem extern (enum (Reg (reg Reg)) @@ -396,13 +540,14 @@ (extern constructor put_in_reg_mem put_in_reg_mem) ;; Addressing modes. - +(model SyntheticAmode (type (bv 64))) (type SyntheticAmode extern (enum)) +(spec (synthetic_amode_to_reg_mem am) (provide(= result am))) (decl synthetic_amode_to_reg_mem (SyntheticAmode) RegMem) (extern constructor synthetic_amode_to_reg_mem synthetic_amode_to_reg_mem) -(spec (amode_to_synthetic_amode amode) (provide (= result amode))) +(spec (amode_to_synthetic_amode amode) (provide (= result (:addr amode)))) (decl amode_to_synthetic_amode (Amode) SyntheticAmode) (extern constructor amode_to_synthetic_amode amode_to_synthetic_amode) @@ -417,6 +562,12 @@ ;; An `Amode` represents a possible addressing mode that can be used ;; in instructions. These denote a 64-bit value only. +(model Amode (type + (struct + (addr (bv 64)) + (flags (named MemFlagsData)) + ) +)) (type Amode (enum ;; Immediate sign-extended and a register (ImmReg (simm32 i32) @@ -436,31 +587,35 @@ ;; the given MachLabel. (RipRelative (target MachLabel)))) +(spec (Amode.ImmReg simm32 base flags) + (provide + ; Address calculation. + (= (:addr result) (bvadd base (sign_ext 64 simm32))) + + ; Flags passthrough. + (= (:flags result) flags) + ) +) + +(spec (Amode.ImmRegRegShift simm32 base index shift flags) + (provide + ; Address calculation. + (= + (:addr result) + (bvadd + (bvadd base (sign_ext 64 simm32)) + (bvshl index (zero_ext 64 shift)) + ) + ) + + ; Flags passthrough. + (= (:flags result) flags) + ) +) + ;; Model an Amode as a combination of flags and the calculated 64-bit address. ;; 16 bits 64 bits ;; [ flags | address ] -(model Amode (type (bv 80))) - -(spec (Amode.ImmReg simm base flags) - (provide (= result (concat flags (bvadd base (sign_ext 64 simm))))) - (require - (= (widthof simm) 32) - (= (widthof base) 64) - (= (widthof flags) 16))) - -(spec (Amode.ImmRegRegShift simm base index shift flags) - (provide - (= result - (concat flags - (bvadd - (bvadd base (sign_ext 64 simm)) - (bvshl index (zero_ext 64 shift)))))) - (require - (= (widthof simm) 32) - (= (widthof base) 64) - (= (widthof index) 64) - (= (widthof shift) 8) - (= (widthof flags) 16))) ;; A helper to both check that the `Imm64` and `Offset32` values sum to less ;; than 32-bits AND return this summed `u32` value. Also, the `Imm64` will be @@ -502,10 +657,7 @@ ;; In the future if mid-end optimizations fold constants into `Offset32` then ;; this in theory can "simply" delegate to the `amode_imm_reg` helper, and ;; below can delegate to `amode_imm_reg_reg_shift`, or something like that. -(spec (to_amode flags val offset) - (provide (= result (concat flags (bvadd val (sign_ext 64 offset))))) - (require - (= (widthof val) 64))) +(attr to_amode (veri chain)) (decl to_amode (MemFlagsData Value Offset32) SyntheticAmode) (rule 0 (to_amode flags base offset) (amode_imm_reg flags base offset)) @@ -528,10 +680,7 @@ ;; In other words this function's job is to find constants and then defer to ;; `amode_imm_reg*`. ;; -(spec (to_amode_add flags x y offset) - (provide (= result (concat flags (bvadd (bvadd (sign_ext 64 x) (sign_ext 64 y)) (sign_ext 64 offset)))))) -(instantiate to_amode_add - ((args (bv 16) (bv 64) (bv 64) (bv 32)) (ret (bv 80)) (canon (bv 64)))) +(attr to_amode_add (veri chain)) (decl to_amode_add (MemFlagsData Value Value Offset32) Amode) (rule to_amode_add_base_case 0 (to_amode_add flags x y offset) @@ -558,24 +707,14 @@ ;; Final cases of amode lowering. Does not hunt for constants and only attempts ;; to pattern match add-of-shifts to generate fancier `ImmRegRegShift` modes, ;; otherwise falls back on `ImmReg`. -(spec (amode_imm_reg flags x offset) - (provide (= result (concat flags (bvadd (sign_ext 64 x) (sign_ext 64 offset)))))) -(instantiate amode_imm_reg - ((args (bv 16) (bv 64) (bv 32)) (ret (bv 80)) (canon (bv 64)))) +(attr amode_imm_reg (veri chain)) (decl amode_imm_reg (MemFlagsData Value Offset32) Amode) (rule amode_imm_reg_base 0 (amode_imm_reg flags base offset) (Amode.ImmReg offset base flags)) (rule amode_imm_reg_iadd 1 (amode_imm_reg flags (iadd _ x y) offset) (amode_imm_reg_reg_shift flags x y offset)) -(spec (amode_imm_reg_reg_shift flags x y offset) - (provide (= result (concat flags (bvadd (sign_ext 64 (bvadd x y)) (sign_ext 64 offset))))) - (require - (= (widthof flags) 16) - (= (widthof x) (widthof y)) - (= (widthof offset) 32))) -(instantiate amode_imm_reg_reg_shift - ((args (bv 16) (bv 64) (bv 64) (bv 32)) (ret (bv 80)) (canon (bv 64)))) +(attr amode_imm_reg_reg_shift (veri chain)) (decl amode_imm_reg_reg_shift (MemFlagsData Value Value Offset32) Amode) (rule amode_imm_reg_reg_shift_no_shift 0 (amode_imm_reg_reg_shift flags x y offset) (Amode.ImmRegRegShift offset x y 0 flags)) ;; 0 == y<<0 == "no shift" @@ -616,6 +755,11 @@ RotateLeft RotateRight)) +(model Imm8Gpr (type + (struct (Imm8 (bv 8)) + (isReg Bool) + (reg (bv 64)) + ))) (type Imm8Gpr (enum (Imm8 (imm u8)) (Gpr (reg Gpr)))) @@ -629,6 +773,21 @@ ;; As a side effect, this marks the value as used. ;; ;; This is used when lowering various shifts and rotates. +(spec (put_masked_in_imm8_gpr amt ty) (provide + (= (:Imm8 result) + (bvand + (conv_to 8 amt) + (switch (:bits ty) + (8 #x07) + (16 #x0f) + (32 #x1f) + (64 #x3f) + ) + ) + ) + (= (:isReg result) false) + (= (:reg result) #x0000000000000000) + )) (decl put_masked_in_imm8_gpr (Value Type) Imm8Gpr) (rule 2 (put_masked_in_imm8_gpr (u64_from_iconst amt) ty) (Imm8Gpr.Imm8 (u64_truncate_into_u8 (u64_and amt (shift_mask ty))))) @@ -689,15 +848,24 @@ (extern constructor encode_round_imm encode_round_imm) ;;;; Newtypes for Different Register Classes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - +(model Gpr (type (bv 64))) (type Gpr (primitive Gpr)) + +(model WritableGpr (type (bv))) (type WritableGpr (primitive WritableGpr)) (type OptionWritableGpr (primitive OptionWritableGpr)) + +(model GprMem (type + (struct (data (bv 64)) + (isMem Bool))) +) (type GprMem extern (enum)) (type GprMem8 extern (enum)) (type GprMem16 extern (enum)) (type GprMem32 extern (enum)) (type GprMem64 extern (enum)) + +(model GprMemImm (type (bv))) (type GprMemImm extern (enum)) (type GprMemImm8 extern (enum)) (type GprMemImm16 extern (enum)) @@ -749,10 +917,12 @@ (extern constructor writable_xmm_to_xmm writable_xmm_to_xmm) ;; Convert a `WritableGpr` to an `Gpr`. +(spec (writable_gpr_to_gpr arg) (provide (= result arg))) (decl writable_gpr_to_gpr (WritableGpr) Gpr) (extern constructor writable_gpr_to_gpr writable_gpr_to_gpr) ;; Convert an `Gpr` to a `Reg`. +(spec (gpr_to_reg arg) (provide (= result arg))) (decl gpr_to_reg (Gpr) Reg) (extern constructor gpr_to_reg gpr_to_reg) @@ -796,6 +966,7 @@ (extern constructor xmm_mem_imm_to_xmm_mem_aligned_imm xmm_mem_imm_to_xmm_mem_aligned_imm) ;; Allocate a new temporary GPR register. +(spec (temp_writable_gpr) (provide true)) (decl temp_writable_gpr () WritableGpr) (extern constructor temp_writable_gpr temp_writable_gpr) @@ -816,6 +987,7 @@ ;; Construct a new `GprMemImm` from the given `RegMemImm`. ;; ;; Asserts that the `RegMemImm`'s register, if any, is an GPR register. +(spec (gpr_mem_imm_new arg) (provide (= result arg))) (decl gpr_mem_imm_new (RegMemImm) GprMemImm) (extern constructor gpr_mem_imm_new gpr_mem_imm_new) @@ -846,12 +1018,17 @@ ;; Construct a new `Gpr` from a `Reg`. ;; ;; Asserts that the register is a GPR. +(spec (gpr_new arg) (provide (= result arg))) (decl gpr_new (Reg) Gpr) (extern constructor gpr_new gpr_new) ;; Construct a new `GprMem` from a `RegMem`. ;; ;; Asserts that the `RegMem`'s register, if any, is a GPR. +(spec (reg_mem_to_gpr_mem rm) + (provide + (= (:data result) rm) + (= (:isMem result) true))) (decl reg_mem_to_gpr_mem (RegMem) GprMem) (extern constructor reg_mem_to_gpr_mem reg_mem_to_gpr_mem) @@ -1127,8 +1304,7 @@ (extern constructor put_in_xmm_mem_imm put_in_xmm_mem_imm) ;; Construct an `InstOutput` out of a single GPR register. -(spec (output_gpr x) - (provide (= result (conv_to (widthof result) x)))) + (decl output_gpr (Gpr) InstOutput) (rule (output_gpr x) (output_reg (gpr_to_reg x))) @@ -1151,6 +1327,11 @@ (gpr_new (value_regs_get regs n))) ;; Convert a `Gpr` to an `Imm8Gpr`. +(spec (gpr_to_imm8_gpr gpr) (provide + (= (:reg result) gpr) + (= (:isReg result) true) + (= (:Imm8 result) #x00) +)) (decl gpr_to_imm8_gpr (Gpr) Imm8Gpr) (rule (gpr_to_imm8_gpr gpr) (Imm8Gpr.Gpr gpr)) @@ -1175,9 +1356,7 @@ (decl is_xmm_type (Type) Type) (extractor (is_xmm_type ty) (and (type_register_class (RegisterClass.Xmm)) ty)) -(spec (is_gpr_type arg) - (provide (= result arg)) - (require (<= arg 64))) +(spec (is_gpr_type arg) (provide (= result arg))) (decl is_gpr_type (Type) Type) (extractor (is_gpr_type ty) (and (type_register_class (RegisterClass.Gpr _)) ty)) @@ -1288,6 +1467,14 @@ ;;;; Helpers for Merging and Sinking Immediates/Loads ;;;;;;;;;;;;;;;;;;;;;;;;; ;; Generate a mask for the bit-width of the given type +(spec (shift_mask ty) + (provide + (= result + (switch (:bits ty) + (8 #x07) ;; 3‑bit mask for 8‑bit shifts + (16 #x0f) ;; 4‑bit mask for 16‑bit shifts + (32 #x1f) ;; 5‑bit mask for 32‑bit shifts + (64 #x3f))))) (decl shift_mask (Type) u8) (extern constructor shift_mask shift_mask) @@ -1300,6 +1487,7 @@ (extern extractor simm32_from_value simm32_from_value) ;; A load that can be sunk into another operation. +(model SinkableLoad (type (bv))) (type SinkableLoad extern (enum)) ;; Extract a `SinkableLoad` that works with `RegMemImm.Mem` from a value @@ -1309,9 +1497,9 @@ ;; pervasively used with operations that load a minimum of 32-bits. For ;; instructions which load exactly the type width necessary use ;; `sinkable_load_exact`. +(spec (sinkable_load sl) (provide (= result sl))) (decl sinkable_load (SinkableLoad) Value) -(spec (sinkable_load inst) - (provide (= result inst))) + (extern extractor sinkable_load sinkable_load) ;; Same as `sinkable_load` except that all type widths of loads are supported. @@ -1326,6 +1514,7 @@ ;; This is a side-effectful operation that notifies the context that the ;; instruction that produced the `SinkableImm` has been sunk into another ;; instruction, and no longer needs to be lowered. +(spec (sink_load sl) (provide (= result sl))) (decl sink_load (SinkableLoad) SyntheticAmode) (extern constructor sink_load sink_load) @@ -1403,8 +1592,7 @@ (rule (sink_load_to_gpr_mem load) (RegMem.Mem load)) (decl sink_load_to_reg_mem_imm (SinkableLoad) RegMemImm) -(spec (sink_load_to_reg_mem_imm load) - (provide (= result load))) + (rule (sink_load_to_reg_mem_imm load) (RegMemImm.Mem load)) ;;;; Helpers for constructing and emitting an `MInst` ;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1492,32 +1680,24 @@ (type ExtendKind (enum Sign Zero)) -(model ExtMode (enum - (BL #b000) - (BQ #b001) - (WL #b010) - (WQ #b011) - (LQ #b100) -)) (type ExtMode extern (enum BL BQ WL WQ LQ)) ;; `ExtMode::new` - -(spec (ext_mode x y) - (provide (= result (switch x - (#x0008 (switch y - (#x0020 (ExtMode.BL)) - (#x0040 (ExtMode.BQ)) - )) - (#x0010 (switch y - (#x0020 (ExtMode.WL)) - (#x0040 (ExtMode.WQ)) - )) - (#x0020 (switch y - (#x0040 (ExtMode.LQ)) - )) - )) - ) +(spec (ext_mode src_bits dst_bits) + ;; only extend from shorter to longer bits + (require (bvule src_bits dst_bits)) + (provide + ;; mirror Rust’s ExtMode::new match: + (= result + (if (= src_bits #x0008) + (if (= dst_bits #x0020) + (ExtMode.BL) + (ExtMode.BQ)) + (if (= src_bits #x0010) + (if (= dst_bits #x0020) + (ExtMode.WL) + (ExtMode.WQ)) + (ExtMode.LQ))))) ) (decl ext_mode (u16 u16) ExtMode) (extern constructor ext_mode ext_mode) @@ -1559,7 +1739,6 @@ (ext_mode from_bits to_bits) val))) - ;; Do a sign or zero extension of the given `GprMem`. (decl extend (ExtendKind Type ExtMode GprMem) Gpr) @@ -1682,33 +1861,10 @@ (rule 0 (x64_load_xmm (multi_lane _bits _lanes) addr) (x64_movdqu_load (synthetic_amode_to_xmm_mem_128 addr))) (decl x64_mov (SyntheticAmode) Reg) -(spec (x64_mov addr) - (provide (= result (conv_to 64 (load_effect (extract 79 64 addr) 64 (extract 63 0 addr)))))) + (rule (x64_mov addr) (x64_movq_rm addr)) (decl x64_movzx (ExtMode GprMem) Gpr) -(spec (x64_movzx mode src) - (provide - (= result - (conv_to - 64 - (zero_ext - 32 - (load_effect - (extract 79 64 src) - (switch mode - ((ExtMode.BL) 8) - ((ExtMode.BQ) 8) - ((ExtMode.WL) 16) - ((ExtMode.WQ) 16) - ((ExtMode.LQ) 32)) - (extract 63 0 src)))))) - (require (or (= mode (ExtMode.BL)) - (= mode (ExtMode.BQ)) - (= mode (ExtMode.WL)) - (= mode (ExtMode.WQ)) - (= mode (ExtMode.LQ))))) - (rule (x64_movzx (ExtMode.BL) src) (x64_movzbl_rm (gpr_mem_as_gpr_mem_8 src))) (rule (x64_movzx (ExtMode.BQ) src) (x64_movzbq_rm (gpr_mem_as_gpr_mem_8 src))) (rule (x64_movzx (ExtMode.WL) src) (x64_movzwl_rm (gpr_mem_as_gpr_mem_16 src))) @@ -1806,8 +1962,7 @@ (rule (x64_pmovzxdq from) (x64_pmovzxdq_a_or_avx from)) (decl x64_movrm (Type SyntheticAmode Gpr) SideEffectNoResult) -(spec (x64_movrm ty addr data) - (provide (= result (store_effect (extract 79 64 addr) ty (conv_to ty data) (extract 63 0 addr))))) + (rule (x64_movrm $I8 addr data) (x64_movb_mr_mem addr data)) (rule (x64_movrm $I16 addr data) (x64_movw_mr_mem addr data)) (rule (x64_movrm $I32 addr data) (x64_movl_mr_mem addr data)) @@ -1824,7 +1979,6 @@ (rule (x64_xmm_load_const ty const) (x64_load_xmm ty (const_to_synthetic_amode const))) - ;;;; Flag Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; These helpers are used to emit instructions that produce or consume flags. @@ -1887,8 +2041,6 @@ (rule (asm_consumes_flags_returns_gpr (AssemblerOutputs.RetGpr inst gpr)) (ConsumesFlags.ConsumesFlagsReturnsReg inst gpr)) - - ;;;; Instruction Constructors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; These constructors create SSA-style `MInst`s. It is their responsibility to @@ -1934,8 +2086,6 @@ (rule (x64_add_with_flags_paired ty src1 src2) (asm_produce_flags (x64_add_raw ty src1 src2))) - - ;; Helper for creating raw `adc` instructions; Cranelift only uses the 64-bit ;; variant of this instruction. As with `add`, we match 8-bit immediates first; ;; this allows a smaller instruction encoding. @@ -1954,8 +2104,6 @@ (rule (x64_adc_chained ty src1 src2) (asm_chain_flags (x64_adc_raw ty src1 src2))) - - ;; Helper for emitting raw `sub` instructions. (decl x64_sub_raw (Type Gpr GprMemImm) AssemblerOutputs) @@ -1994,8 +2142,6 @@ (rule (x64_sub_with_flags_paired ty src1 src2) (asm_produce_flags (x64_sub_raw ty src1 src2))) - - ;; Helper for creating raw `sbb` instructions; Cranelift only uses the 64-bit ;; variant of this instruction. (decl x64_sbb_raw (Type Gpr GprMemImm) AssemblerOutputs) @@ -2040,8 +2186,6 @@ (rule (x64_sbb_paired_side_effect ty src1 src2) (asm_produce_flags_side_effect (x64_sbb_raw ty src1 src2))) - - ;; Helper for creating `mul` instructions or `imul` instructions (depending ;; on `signed`). For the 8-bit rules, see `x64_mul8`. (decl x64_mul_raw (Type bool Gpr GprMem) AssemblerOutputs) @@ -2118,8 +2262,6 @@ (rule (x64_mul8_with_flags_paired signed src1 src2) (asm_produce_flags (x64_mul8_raw signed src1 src2))) - - ;; Helper for emitting `and` instructions. (decl x64_and (Type Gpr GprMemImm) Gpr) @@ -2144,8 +2286,6 @@ (rule 0 (x64_and ty @ $I32 src1 (is_gpr_mem src2)) (x64_andl_rm src1 (gpr_mem_32_for_ty ty src2))) (rule 0 (x64_and ty @ $I64 src1 (is_gpr_mem src2)) (x64_andq_rm src1 (gpr_mem_64_for_ty ty src2))) - - ;; Helper for emitting raw `or` instructions. (decl x64_or_raw (Type Gpr GprMemImm) AssemblerOutputs) @@ -2183,8 +2323,6 @@ (rule (x64_or_with_flags_paired_side_effect ty src1 src2) (asm_produce_flags_side_effect (x64_or_raw ty src1 src2))) - - ;; Helper for emitting `xor` instructions. (decl x64_xor (Type Gpr GprMemImm) Gpr) @@ -2318,6 +2456,35 @@ (x64_rorx ty src imm)) ;; Helper for creating `shl` instructions. +(spec (x64_shl ty src amt) + (require + (= (:isReg amt) false)) + (provide + (= result + (switch (:bits ty) + (8 + ;; for 8‑bit ops, extract the low‑8, shift, then zero‐extend back to 64 + (zero_ext 64 + (bvshl + (conv_to 8 src) + (:Imm8 amt)))) + (16 + (zero_ext 64 + (bvshl + (conv_to 16 src) + (zero_ext 16 (:Imm8 amt))))) + (32 + (zero_ext 64 + (bvshl + (conv_to 32 src) + (zero_ext 32 (:Imm8 amt))))) + (64 + ;; for 64‑bit ops we just shift the full 64 + (bvshl + src + (zero_ext 64 (:Imm8 amt)))))) + ) +) (decl x64_shl (Type Gpr Imm8Gpr) Gpr) (rule (x64_shl $I8 src1 (Imm8Gpr.Gpr src2)) (x64_shlb_mc src1 src2)) (rule (x64_shl $I8 src1 (Imm8Gpr.Imm8 src2)) (x64_shlb_mi src1 src2)) @@ -2530,7 +2697,6 @@ (rule (consumes_flags_with_producer (ConsumesFlags.ConsumesFlagsReturnsReg flags reg)) (ConsumesFlags.ConsumesFlagsReturnsResultWithProducer flags reg)) - ;; Helpers for creating vector `add` instructions. (decl x64_addss (Xmm XmmMem32) Xmm) (rule (x64_addss src1 src2) (x64_addss_a_or_avx src1 src2)) @@ -2951,8 +3117,6 @@ (if-let true (has_avx)) (x64_vpshufhw_a src imm)) - - ;; Helper for creating `vcvtudq2ps` instructions. (decl x64_vcvtudq2ps (XmmMem128) Xmm) (rule (x64_vcvtudq2ps src) (x64_vcvtudq2ps_a src)) @@ -3082,9 +3246,6 @@ (rule (x64_neg_paired ty src) (asm_produce_flags (x64_neg_raw ty src))) -(spec (x64_lea ty amode) - (provide (= result amode)) - (require (or (= ty 32) (= ty 64)))) (decl x64_lea (Type SyntheticAmode) Gpr) (rule (x64_lea $I16 addr) (x64_leaw_rm addr)) (rule (x64_lea $I32 addr) (x64_leal_rm addr)) @@ -3402,7 +3563,6 @@ (decl x64_vrsqrtss (Xmm XmmMem32) Xmm) (rule (x64_vrsqrtss x y) (x64_vrsqrtss_rvm x y)) - ;; Helper for creating `cvtss2sd` instructions. ;; ;; NB: see `x64_sqrtss` for why this has two args (same reasoning, different op) @@ -3576,16 +3736,6 @@ (x64_por low_gt_and_high_eq high_halves_gt))) (decl x64_add_mem (Type SyntheticAmode Value) SideEffectNoResult) -(spec (x64_add_mem ty addr val) - (provide (= result (store_effect - (extract 79 64 addr) - ty - (conv_to ty (bvadd (load_effect (extract 79 64 addr) ty (extract 63 0 addr)) (conv_to ty val))) - (extract 63 0 addr)) - ) - ) - (require (or (= ty 32) (= ty 64))) -) ;; `add mem, reg` (rule 0 (x64_add_mem $I8 addr val) (x64_addb_mr_mem addr val)) @@ -3826,7 +3976,6 @@ (rule 2 (is_nonzero (band _ a @ (value_type (ty_int (fits_in_64 ty))) b)) (is_nonzero_band ty a b)) - ;; Like `is_nonzero` but with additional specializations for compare ;; operators. We break this out from `is_nonzero` because we want to ;; avoid unbounded recursion. @@ -4122,7 +4271,6 @@ (args BoxAtomic128XchgSeqArgs (atomic128_xchg_seq_args mem_low mem_high input_low input_high dst_low dst_high))) (SideEffectNoResult.Inst (MInst.Atomic128XchgSeq args)))) - (type AtomicRmwSeqOp (enum And Nand @@ -4561,7 +4709,6 @@ (convert Amode XmmMem128 amode_to_xmm_mem_128) (convert Amode XmmMemAligned128 amode_to_xmm_mem_aligned_128) - (convert Reg GprMem8 reg_to_gpr_mem_8) (convert Reg GprMem16 reg_to_gpr_mem_16) (convert Reg GprMem32 reg_to_gpr_mem_32) @@ -4651,8 +4798,6 @@ (decl synthetic_amode_to_gpr_mem (SyntheticAmode) GprMem) -(spec (amode_to_gpr_mem amode) - (provide (= result amode))) (decl amode_to_gpr_mem (Amode) GprMem) (rule (amode_to_gpr_mem amode) (amode_to_synthetic_amode amode)) @@ -4859,3 +5004,64 @@ (decl x64_sequence_point () SideEffectNoResult) (rule (x64_sequence_point) (SideEffectNoResult.Inst (MInst.SequencePoint))) + +(state isa_load + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)) + ) + ) + (default + (not (:active isa_load)) + ) +) + +(state isa_store + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)) + (value (bv 64)) + ) + ) + (default + (and + ; Store is not active. + (not (:active isa_store)) + + ; Must provide a fixed size in the default case, otherwise type + ; inference is underconstrained. + (= (:size_bits isa_store) 1) + ) + ) +) + +(model Reg (type (bv))) + +(spec + (const_to_type_masked_imm8 u ty) + (provide + (= (:Imm8 result) + (bvand + (zero_ext 8 u) + (switch (:bits ty) + (8 #x07) ;; 3‑bit mask for 8‑bit shifts + (16 #x0f) ;; 4‑bit mask for 16‑bit shifts + (32 #x1f) ;; 5‑bit mask for 32‑bit shifts + (64 #x3f) ;; 6‑bit mask for 64‑bit shifts + ) + ) + ) + (= (:isReg result) false) +)) + +(attr x64_alurmi_with_flags_paired (veri chain)) + +(attr x64_alurmi_flags_side_effect (veri chain)) + +(attr x64_alurmi_with_flags_chained (veri chain)) + +(attr lower_icmp_bool (veri chain)) diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle index 8bf69de5eb80..2ad78a4613a0 100644 --- a/cranelift/codegen/src/isa/x64/lower.isle +++ b/cranelift/codegen/src/isa/x64/lower.isle @@ -2,8 +2,6 @@ ;; The main lowering constructor term: takes a clif `Inst` and returns the ;; register(s) within which the lowered instruction's result values live. -(spec (lower arg) - (provide (= result arg))) (decl partial lower (Inst) InstOutput) ;; A variant of the main lowering constructor term, used for branches. @@ -514,7 +512,6 @@ (if-let true (u64_gt ys 0)) (x64_shld ty x y xs)) - ;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `{i,b}64` and smaller. @@ -966,7 +963,6 @@ (rule -1 (lower (has_type (fits_in_64 ty) (rotl _ src amt))) (x64_rotl ty src (put_masked_in_imm8_gpr amt ty))) - ;; `i128`. (rule (lower (has_type $I128 (rotl _ src amt))) @@ -987,7 +983,6 @@ (rule -1 (lower (has_type (fits_in_64 ty) (rotr _ src amt))) (x64_rotr ty src (put_masked_in_imm8_gpr amt ty))) - ;; `i128`. (rule (lower (has_type $I128 (rotr _ src amt))) @@ -1456,7 +1451,6 @@ ;; Extract only the output of the sbb instruction (value_reg (value_regs_get out 1)))) - ;; If the input type is I128 we can `or` the registers, and recurse to the general case. (rule 1 (lower_bmask (fits_in_64 out_ty) $I128 val) @@ -1472,7 +1466,6 @@ (res Gpr (value_regs_get_gpr res 0))) (value_regs res res))) - ;; Call the lower_bmask rule that does all the procssing (rule (lower (has_type out_ty (bmask _ x @ (value_type in_ty)))) (lower_bmask out_ty in_ty x)) @@ -1485,7 +1478,6 @@ (if (ty_int_ref_scalar_64 ty)) (x64_not ty x)) - ;; `i128`. (decl not_i128 (Value) ValueRegs) @@ -1564,7 +1556,7 @@ (b ValueRegs (and_i128 (not_i128 c) f))) (or_i128 a b))) -(rule 4 (lower (has_type (ty_int_ref_scalar_64 ty) (bitselect _ c t f))) +(rule 4 (lower (has_type (ty_int_ref_scalar_64_extract ty) (bitselect _ c t f))) (let ((a Gpr (x64_and ty c t)) (b Gpr (x64_and ty (x64_not ty c) f))) (x64_or ty a b))) @@ -1648,7 +1640,6 @@ (rule (insert_i8x16_lane_pshufd_imm 2) 0b01_00_01_01) (rule (insert_i8x16_lane_pshufd_imm 3) 0b00_01_01_01) - ;; i16x8.replace_lane (rule (lower (insertlane _ vec @ (value_type $I16X8) val (u8_from_uimm8 idx))) (x64_pinsrw vec val idx)) @@ -1764,7 +1755,7 @@ ;;;; Rules for `smin`, `smax`, `umin`, `umax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller. - +(attr cmp_and_choose (veri chain)) (decl cmp_and_choose (Type CC Value Value) ValueRegs) (rule (cmp_and_choose (fits_in_64 ty) cc x y) (let( ;; We need to put x and y in registers explicitly because @@ -2283,7 +2274,6 @@ (clz Gpr (do_clz $I64 $I64 extended))) (x64_sub $I64 clz (RegMemImm.Imm (u32_wrapping_sub 64 (ty_bits ty)))))) - (rule 0 (lower (has_type $I128 (clz _ src))) @@ -2476,13 +2466,11 @@ (final Gpr (x64_shrl_mi mul 24))) final)) - (rule 2 (lower (has_type $I8X16 (popcnt _ src))) (if-let true (has_avx512vl)) (if-let true (has_avx512bitalg)) (x64_vpopcntb src)) - ;; For SSE 4.2 we use Mula's algorithm (https://arxiv.org/pdf/1611.07612.pdf): ;; ;; __m128i count_bytes ( __m128i v) { @@ -2502,7 +2490,6 @@ ;; ;; __m128i lookup = _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); - (rule 1 (lower (has_type $I8X16 (popcnt _ src))) (if-let true (has_ssse3)) (let ((low_mask XmmMem128 (emit_xmm_mem_128_le_const 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f)) @@ -2597,6 +2584,7 @@ hi16))) swap16)) +(attr do_bitrev64 (veri chain)) (decl do_bitrev64 (Type Gpr) Gpr) (rule (do_bitrev64 ty @ $I64 src) (let ((src_ Gpr (do_bitrev32 ty src)) @@ -3000,7 +2988,6 @@ ) (x64_movlhps x0 x1))) - ;; Special case for when the `fma` feature is active and a native instruction ;; can be used. (rule 1 (lower (has_type ty (fma _ x y z))) @@ -3031,7 +3018,6 @@ (rule 1 (fnmadd ty x @ (sinkable_load _) y z) (x64_vfnmadd132 ty y z x)) (rule 2 (fnmadd ty x y @ (sinkable_load _) z) (x64_vfnmadd132 ty x z y)) - (rule 2 (lower (has_type ty (fma _ x y (fneg _ z)))) (if-let true (use_fma)) (fmsub ty x y z)) @@ -3058,7 +3044,6 @@ (rule 1 (fnmsub ty x @ (sinkable_load _) y z) (x64_vfnmsub132 ty y z x)) (rule 2 (fnmsub ty x y @ (sinkable_load _) z) (x64_vfnmsub132 ty x z y)) - ;; Rules for `load*` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; In order to load a value from memory to a GPR register, we may need to extend @@ -4003,7 +3988,6 @@ ) (x64_paddb evens odds))) - (rule 1 (lower (has_type $I16X8 (iadd_pairwise _ x y))) (if-let true (has_ssse3)) (x64_phaddw x y)) @@ -4290,7 +4274,6 @@ ) (x64_pshufd val 0b00_00_10_00))) - ;; We're missing a `unarrow` case for $I64X2 ;; https://github.com/bytecodealliance/wasmtime/issues/4734 @@ -4713,7 +4696,6 @@ (decl shufps_rev_imm(u8) Immediate) (extern extractor shufps_rev_imm shufps_rev_imm) - ;; If `lhs` and `rhs` are the same we can use a single PSHUFB to shuffle the XMM ;; register. We statically build `constructed_mask` to zero out any unknown lane ;; indices (may not be completely necessary: verification could fail incorrect @@ -5184,7 +5166,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ block (u64_from_imm64 idx))) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (x64_label_address succ_label))) @@ -5198,3 +5180,7 @@ (rule (lower (sequence_point)) (side_effect (x64_sequence_point))) + +(attr lower_select_icmp (veri chain)) + +(attr cmp_zero_i128 (veri chain)) diff --git a/cranelift/codegen/src/lib.rs b/cranelift/codegen/src/lib.rs index cbf47a02fa9a..08de586ac985 100644 --- a/cranelift/codegen/src/lib.rs +++ b/cranelift/codegen/src/lib.rs @@ -76,8 +76,8 @@ pub use crate::machinst::buffer::{ pub use crate::machinst::{ CallInfo, CompiledCode, Final, FrameLayout, MachBuffer, MachBufferDebugTagList, MachBufferFinalized, MachBufferFrameLayout, MachDebugTagPos, MachInst, MachInstEmit, - MachInstEmitState, MachLabel, RealReg, Reg, RelocDistance, TextSectionBuilder, VCodeConstant, - VCodeConstantData, VCodeConstants, VCodeInst, Writable, + MachInstEmitState, MachLabel, RealReg, Reg, RegClass, RelocDistance, TextSectionBuilder, + VCodeConstant, VCodeConstantData, VCodeConstants, VCodeInst, Writable, }; mod alias_analysis; diff --git a/cranelift/codegen/src/opts/bitops.isle b/cranelift/codegen/src/opts/bitops.isle index 0711c14eb6c0..f3b47772f972 100644 --- a/cranelift/codegen/src/opts/bitops.isle +++ b/cranelift/codegen/src/opts/bitops.isle @@ -114,7 +114,7 @@ ;; (ne ty (iconst 0) v) is also canonicalized into this form via another rule (rule (simplify (ne cty v (iconst_u _ 0))) (if-let c (truthy v)) - (if-let (value_type (ty_int_ref_scalar_64 ty)) c) + (if-let (value_type (ty_int_ref_scalar_64_extract ty)) c) (ne cty c (iconst_u ty 0))) diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle index 8c9c33321a3e..54abf1cd4f80 100644 --- a/cranelift/codegen/src/prelude.isle +++ b/cranelift/codegen/src/prelude.isle @@ -7,28 +7,13 @@ ;;;; Primitive and External Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `()` +(model Unit (type (bv 1))) (type Unit (primitive Unit)) -(model Unit (type Unit)) (decl pure unit () Unit) (extern constructor unit unit) - -(model bool (type Bool)) - -(model u8 (type (bv 8))) -(model u16 (type (bv 16))) -(model u32 (type (bv 32))) -(model u64 (type (bv 64))) -(model usize (type (bv))) - -(model i8 (type (bv 8))) -(model i16 (type (bv 16))) -(model i32 (type (bv 32))) -(model i64 (type (bv 64))) - ;; `cranelift-entity`-based identifiers. - -(model Type (type Int)) +(model Type (type (struct (bits Int)))) (type Type (primitive Type)) (model Value (type (bv))) @@ -41,7 +26,6 @@ (type Inst (primitive Inst)) ;; Match the instruction that defines the given value, if any. -(spec (def_inst arg) (provide (= result arg))) (decl def_inst (Inst) Value) (extern extractor def_inst def_inst) @@ -49,7 +33,7 @@ (type ValueSlice (primitive ValueSlice)) ;; Extract the type of a `Value`. -(spec (value_type arg) (provide (= arg (widthof result)))) +(spec (value_type ty) (provide (= (:bits ty) (widthof result)))) (decl value_type (Type) Value) (extern extractor infallible value_type value_type) @@ -143,7 +127,7 @@ (decl pure i64_sextend_u64 (Type u64) i64) (extern constructor i64_sextend_u64 i64_sextend_u64) -(spec (i64_sextend_imm64 ty a) (provide (= result (sign_ext 64 (conv_to ty a))))) +(spec (i64_sextend_imm64 ty a) (provide (= result (sign_ext 64 (conv_to (:bits ty) a))))) (decl pure i64_sextend_imm64 (Type Imm64) i64) (extern constructor i64_sextend_imm64 i64_sextend_imm64) @@ -309,13 +293,12 @@ (extern constructor ty_smax ty_smax) ;; Get the bit width of a given type. -(spec (ty_bits x) (provide (= result (int2bv 8 x)))) +(spec (ty_bits ty) (provide (= result (int2bv 8 (:bits ty))))) (decl pure ty_bits (Type) u8) (extern constructor ty_bits ty_bits) ;; Get the bit width of a given type. -(spec (ty_bits_u16 x) - (provide (= result (int2bv 16 x)))) +(spec (ty_bits_u16 ty) (provide (= result (int2bv 16 (:bits ty))))) (decl pure ty_bits_u16 (Type) u16) (extern constructor ty_bits_u16 ty_bits_u16) @@ -336,6 +319,7 @@ (extern constructor ty_lane_count ty_lane_count) ;; Get the byte width of a given type. +(spec (ty_bytes ty) (provide (= result (bits2bytes! (int2bv 16 (:bits ty)))))) (decl pure ty_bytes (Type) u16) (extern constructor ty_bytes ty_bytes) @@ -362,9 +346,10 @@ ;;;; `cranelift_codegen::ir::MemFlags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Provide model for the MemFlags type (declared in generated clif_lower.isle). -(model MemFlags (type (bv 16))) ;; Insert or reuse the trusted `MemFlagsData` value in the DFG's `MemFlagsSet`. +(spec (mem_flags_trusted) + (provide (:aligned result) (= (:trapcode result) #b1111))) (decl mem_flags_trusted () MemFlags) (extern constructor mem_flags_trusted mem_flags_trusted) @@ -382,6 +367,7 @@ ;; Determine if flags specify little- or native-endian. ;; Takes a MemFlags entity and returns the resolved MemFlagsData. +(spec (little_or_native_endian x) (provide (= result x))) (decl little_or_native_endian (MemFlagsData) MemFlags) (extern extractor little_or_native_endian little_or_native_endian) @@ -416,16 +402,16 @@ ;;;; Helper Clif Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; An extractor that only matches types that can fit in 16 bits. -(spec (fits_in_16 arg) - (provide (= result arg)) - (require (<= arg 16))) +(spec (fits_in_16 ty) + (match (<= (:bits result) 16)) + (provide (= result ty))) (decl fits_in_16 (Type) Type) (extern extractor fits_in_16 fits_in_16) ;; An extractor that only matches types that can fit in 32 bits. -(spec (fits_in_32 arg) - (provide (= result arg)) - (require (<= arg 32))) +(spec (fits_in_32 ty) + (match (<= (:bits result) 32)) + (provide (= result ty))) (decl fits_in_32 (Type) Type) (extern extractor fits_in_32 fits_in_32) @@ -434,9 +420,9 @@ (extern extractor lane_fits_in_32 lane_fits_in_32) ;; An extractor that only matches types that can fit in 64 bits. -(spec (fits_in_64 arg) - (provide (= result arg)) - (require (<= arg 64))) +(spec (fits_in_64 ty) + (match (<= (:bits result) 64)) + (provide (= result ty))) (decl fits_in_64 (Type) Type) (extern extractor fits_in_64 fits_in_64) @@ -445,10 +431,16 @@ (extern extractor ty_16 ty_16) ;; An extractor that only matches types that fit in exactly 32 bits. +(spec (ty_32 ty) + (match (= 32 (:bits result))) + (provide (= result ty))) (decl ty_32 (Type) Type) (extern extractor ty_32 ty_32) ;; An extractor that only matches types that fit in exactly 64 bits. +(spec (ty_64 ty) + (match (= 64 (:bits result))) + (provide (= result ty))) (decl ty_64 (Type) Type) (extern extractor ty_64 ty_64) @@ -458,17 +450,32 @@ ;; A pure constructor/extractor that only matches scalar integers, and ;; references that can fit in 64 bits. -(spec (ty_int_ref_scalar_64 arg) - (provide (= result arg)) - (require (<= arg 64))) +(spec (ty_int_ref_scalar_64 ty) + (provide + (or + (= (:bits ty) 8) + (= (:bits ty) 16) + (= (:bits ty) 32) + (= (:bits ty) 64)) + (= result ty))) (decl pure partial ty_int_ref_scalar_64 (Type) Type) (extern constructor ty_int_ref_scalar_64 ty_int_ref_scalar_64) -(extern extractor ty_int_ref_scalar_64 ty_int_ref_scalar_64_extract) + +(spec (ty_int_ref_scalar_64_extract ty) + (match + (or + (= (:bits result) 8) + (= (:bits result) 16) + (= (:bits result) 32) + (= (:bits result) 64))) + (provide (= result ty))) +(decl pure partial ty_int_ref_scalar_64_extract (Type) Type) +(extern extractor ty_int_ref_scalar_64_extract ty_int_ref_scalar_64_extract) ;; An extractor that matches 32- and 64-bit types only. -(spec (ty_32_or_64 arg) - (provide (= result arg)) - (require (or (= arg 32) (= arg 64)))) +(spec (ty_32_or_64 ty) + (match (or (= (:bits result) 32) (= (:bits result) 64))) + (provide (= result ty))) (decl ty_32_or_64 (Type) Type) (extern extractor ty_32_or_64 ty_32_or_64) @@ -493,7 +500,7 @@ (extern extractor ty_int_ref_16_to_64 ty_int_ref_16_to_64) ;; An extractor that only matches integers. -(spec (ty_int a) (provide (= result a))) +(spec (ty_int arg) (provide (= arg result))) (decl ty_int (Type) Type) (extern extractor ty_int ty_int) @@ -502,10 +509,16 @@ (extern extractor ty_scalar ty_scalar) ;; An extractor that only matches scalar floating-point types. +(spec (ty_scalar_float ty) + (match (or (= (:bits result) 32) (= (:bits result) 64))) + (provide (= result ty))) (decl ty_scalar_float (Type) Type) (extern extractor ty_scalar_float ty_scalar_float) ;; An extractor that matches scalar floating-point types or vector types. +(spec (ty_float_or_vec ty) + (match (or (= (:bits result) 32) (= (:bits result) 64) (= (:bits result) 128))) + (provide (= result ty))) (decl ty_float_or_vec (Type) Type) (extern extractor ty_float_or_vec ty_float_or_vec) @@ -519,11 +532,13 @@ (extern constructor ty_vector_not_float ty_vector_not_float) ;; A pure constructor/extractor that only matches 64-bit vector types. +(attr ty_vec64 (tag vector)) (decl pure partial ty_vec64 (Type) Type) (extern constructor ty_vec64 ty_vec64_ctor) (extern extractor ty_vec64 ty_vec64) ;; An extractor that only matches 128-bit vector types. +(attr ty_vec128 (tag vector)) (decl ty_vec128 (Type) Type) (extern extractor ty_vec128 ty_vec128) @@ -533,6 +548,7 @@ ;; An extractor that only matches dynamic vector types with a 64-bit ;; base type. +(attr ty_dyn_vec64 (tag vector)) (decl ty_dyn_vec64 (Type) Type) (extern extractor ty_dyn_vec64 ty_dyn_vec64) @@ -577,6 +593,9 @@ (extern extractor infallible u64_from_imm64 u64_from_imm64) ;; Extract a `u64` from an `Imm64` which is not zero. +(spec (nonzero_u64_from_imm64 n) + (match (not (bv_is_zero! result))) + (provide (= n result))) (decl nonzero_u64_from_imm64 (u64) Imm64) (extern extractor nonzero_u64_from_imm64 nonzero_u64_from_imm64) @@ -597,10 +616,12 @@ (extern extractor infallible u16_from_ieee16 u16_from_ieee16) ;; Extract a `u32` from an `Ieee32`. +(spec (u32_from_ieee32 arg) (provide (= result arg))) (decl u32_from_ieee32 (u32) Ieee32) (extern extractor infallible u32_from_ieee32 u32_from_ieee32) ;; Extract a `u64` from an `Ieee64`. +(spec (u64_from_ieee64 arg) (provide (= result arg))) (decl u64_from_ieee64 (u64) Ieee64) (extern extractor infallible u64_from_ieee64 u64_from_ieee64) @@ -637,26 +658,79 @@ ;; This is a direct import of `IntCC::unsigned`. ;; Get the corresponding IntCC with the signed component removed. ;; For conditions without a signed component, this is a no-op. +(spec (intcc_unsigned cc) + (provide + (= result + (match cc + ((Equal) (IntCC.Equal)) + ((NotEqual) (IntCC.NotEqual)) + ((SignedGreaterThanOrEqual) (IntCC.UnsignedGreaterThanOrEqual)) + ((SignedGreaterThan) (IntCC.UnsignedGreaterThan)) + ((SignedLessThanOrEqual) (IntCC.UnsignedLessThanOrEqual)) + ((SignedLessThan) (IntCC.UnsignedLessThan)) + ((UnsignedGreaterThanOrEqual) (IntCC.UnsignedGreaterThanOrEqual)) + ((UnsignedGreaterThan) (IntCC.UnsignedGreaterThan)) + ((UnsignedLessThanOrEqual) (IntCC.UnsignedLessThanOrEqual)) + ((UnsignedLessThan) (IntCC.UnsignedLessThan)) + ) + ) + ) +) (decl pure intcc_unsigned (IntCC) IntCC) (extern constructor intcc_unsigned intcc_unsigned) ;; Pure constructor that only matches signed integer cond codes. -(spec (signed_cond_code c) - (provide (= result c)) - (require (and (bvuge c #x02) (bvule c #x05)))) +(spec (signed_cond_code cc) + (match + (match cc + ((Equal) false) + ((NotEqual) false) + ((SignedGreaterThanOrEqual) true) + ((SignedGreaterThan) true) + ((SignedLessThanOrEqual) true) + ((SignedLessThan) true) + ((UnsignedGreaterThanOrEqual) false) + ((UnsignedGreaterThan) false) + ((UnsignedLessThanOrEqual) false) + ((UnsignedLessThan) false) + ) + ) + (provide (= result cc)) +) (decl pure partial signed_cond_code (IntCC) IntCC) (extern constructor signed_cond_code signed_cond_code) + +(spec (unsigned_cond_code cc) + (match + (match cc + ((Equal) true) + ((NotEqual) true) + ((SignedGreaterThanOrEqual) false) + ((SignedGreaterThan) false) + ((SignedLessThanOrEqual) false) + ((SignedLessThan) false) + ((UnsignedGreaterThanOrEqual) true) + ((UnsignedGreaterThan) true) + ((UnsignedLessThanOrEqual) true) + ((UnsignedLessThan) true) + ) + ) + (provide (= result cc)) +) (decl pure partial unsigned_cond_code (IntCC) IntCC) (extern constructor unsigned_cond_code unsigned_cond_code) ;;;; Helpers for Working with TrapCode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - +(spec (trap_code_division_by_zero) (provide (= result (TrapCode.INTEGER_DIVISION_BY_ZERO)))) (decl pure trap_code_division_by_zero () TrapCode) (extern constructor trap_code_division_by_zero trap_code_division_by_zero) +(spec (trap_code_integer_overflow) (provide (= result (TrapCode.INTEGER_OVERFLOW)))) (decl pure trap_code_integer_overflow () TrapCode) (extern constructor trap_code_integer_overflow trap_code_integer_overflow) +(spec (trap_code_bad_conversion_to_integer) + (provide (= result (TrapCode.BAD_CONVERSION_TO_INTEGER)))) (decl pure trap_code_bad_conversion_to_integer () TrapCode) (extern constructor trap_code_bad_conversion_to_integer trap_code_bad_conversion_to_integer) @@ -665,28 +739,64 @@ (convert Offset32 i32 offset32_to_i32) (convert i32 Offset32 i32_to_offset32) -;;;; Common Term Signatures ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(form - bv_unary_8_to_64 - ((args (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 64)) (canon (bv 64))) -) +;;;; Models and specs for derived ISLE numerics ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(form - bv_binary_8_to_64 - ((args (bv 8) (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16) (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 32) (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) - -(form - bv_ternary_8_to_64 - ((args (bv 8) (bv 8) (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16) (bv 16) (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 32) (bv 32) (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64) (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) +(model bool (type Bool)) +(model u8 (type (bv 8))) +(model u16 (type (bv 16))) +(model u32 (type (bv 32))) +(model u64 (type (bv 64))) +(model u128 (type (bv 128))) +(model usize (type Int)) +(model i8 (type (bv 8))) +(model i16 (type (bv 16))) +(model i32 (type (bv 32))) +(model i64 (type (bv 64))) +(model isize (type Int)) + +(model I8 (const (struct (bits 8)))) +(model I16 (const (struct (bits 16)))) +(model I32 (const (struct (bits 32)))) +(model I64 (const (struct (bits 64)))) +(model I128 (const (struct (bits 128)))) +(model F32 (const (struct (bits 32)))) +(model F64 (const (struct (bits 64)))) +(model I8X16 (const (struct (bits 128)))) + +(spec (i64_neg x) (provide (= result (bvneg x)))) +(spec (u64_sub x y) (provide (= result (bvsub x y)))) +(spec (u64_shl x y) (provide (= result (bvshl x y)))) +(spec (u64_eq x y) (provide (= result (= x y)))) +(spec (u64_is_odd x) (provide (= result (= (conv_to 1 x) #b1)))) +(spec (i64_checked_neg x) + (match (not (= x #x8000000000000000))) + (provide (= result (bvneg x)))) +(spec (i64_cast_unsigned x) (provide (= result x))) +(spec (i32_checked_add x y) + (match + (or + (not (= (bvslt x #x00000000) (bvslt y #x00000000))) ; different sign inputs + (= (bvslt x #x00000000) (bvslt (bvadd x y) #x00000000)))) ; same sign sum + (provide (= result (bvadd x y)))) +(spec (u8_into_u16 x) (provide (= result (zero_ext 16 x)))) +(spec (u8_into_u32 x) (provide (= result (zero_ext 32 x)))) +(spec (u8_into_u64 x) (provide (= result (zero_ext 64 x)))) +(spec (u16_into_u32 x) (provide (= result (zero_ext 32 x)))) +(spec (u16_into_u64 x) (provide (= result (zero_ext 64 x)))) +(spec (u32_into_u64 x) (provide (= result (zero_ext 64 x)))) +(spec (i8_into_i16 x) (provide (= result (sign_ext 16 x)))) +(spec (i8_into_i32 x) (provide (= result (sign_ext 32 x)))) +(spec (i8_into_i64 x) (provide (= result (sign_ext 64 x)))) +(spec (i16_into_i32 x) (provide (= result (sign_ext 32 x)))) +(spec (i16_into_i64 x) (provide (= result (sign_ext 64 x)))) +(spec (i32_into_i64 x) (provide (= result (sign_ext 64 x)))) +(spec (u32_from_u64 x) + (match (bvule result #x00000000ffffffff)) + (provide (= x (conv_to 32 result)))) +(spec (u64_wrapping_shl x y) + (provide (= result + (bvshl x + (zero_ext 32 (bvand y #x0000003f)))))) ; shift by y mod 64 +(spec (u64_wrapping_sub x y) (provide (= result (bvsub x y)))) +(spec (u64_wrapping_add x y) (provide (= result (bvadd x y)))) +(spec (u64_gt x y) (provide (bvugt x y))) diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle index baae79a3df7d..87f61e853c23 100644 --- a/cranelift/codegen/src/prelude_lower.isle +++ b/cranelift/codegen/src/prelude_lower.isle @@ -6,11 +6,20 @@ ;; ISLE representation of `Vec` (type VecMask extern (enum)) +(model ValueRegs + (type + (struct + (lo (named Reg)) + (hi (named Reg)) + ) + ) +) (type ValueRegs (primitive ValueRegs)) (type WritableValueRegs (primitive WritableValueRegs)) (type ValueRegsVec extern (enum)) ;; Instruction lowering result: a vector of `ValueRegs`. +(model InstOutput (type (bv))) (type InstOutput (primitive InstOutput)) ;; Type to hold multiple Regs @@ -25,8 +34,9 @@ ;;;; Registers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(model Reg (type (bv))) (type Reg (primitive Reg)) + +(model WritableReg (type (bv))) (type WritableReg (primitive WritableReg)) (type OptionWritableReg (primitive OptionWritableReg)) (type VecReg extern (enum)) @@ -34,8 +44,7 @@ (type PReg (primitive PReg)) ;; Construct a `ValueRegs` of one register. -(spec (value_reg arg) - (provide (= result arg))) +(spec (value_reg arg) (provide (= (:lo result) arg))) (decl value_reg (Reg) ValueRegs) (extern constructor value_reg value_reg) @@ -44,6 +53,9 @@ (extern constructor writable_value_reg writable_value_reg) ;; Construct a `ValueRegs` of two registers. +(spec (value_regs x y) + (provide (= x (:lo result)) + (= y (:hi result)))) (decl value_regs (Reg Reg) ValueRegs) (extern constructor value_regs value_regs) @@ -56,12 +68,20 @@ (extern constructor value_regs_invalid value_regs_invalid) ;; Construct an empty `InstOutput`. +(spec (output_none) (provide (= result #b1))) (decl output_none () InstOutput) (extern constructor output_none output_none) ;; Construct a single-element `InstOutput`. (spec (output arg) - (provide (= arg (conv_to (widthof arg) result)))) + (provide + ; Two input registers are the same width. + (= (widthof (:lo arg)) (widthof (:hi arg))) + + ; Output is the concatenation of the two registers. + (let + ((cat (concat (:hi arg) (:lo arg)))) + (= cat (conv_to (widthof cat) result))))) (decl output (ValueRegs) InstOutput) (extern constructor output output) @@ -70,14 +90,6 @@ (extern constructor output_pair output_pair) ;; Construct a single-element `InstOutput` from a single register. -(spec (output_reg arg) - (provide (= result (conv_to (widthof result) arg)))) -(instantiate output_reg - ((args (bv 64)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 64)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 64)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 64)) (canon (bv 64))) -) (decl output_reg (Reg) InstOutput) (rule output_reg (output_reg reg) (output (value_reg reg))) @@ -90,6 +102,7 @@ (extern constructor output_vec output_vec) ;; Get a temporary register for writing. +(spec (temp_writable_reg ty) (provide (= (:bits ty) (widthof result)))) (decl temp_writable_reg (Type) WritableReg) (extern constructor temp_writable_reg temp_writable_reg) @@ -121,14 +134,18 @@ ;; ;; As a side effect, this marks the value as used. (spec (put_in_reg arg) - (provide (= result (conv_to 64 arg)))) + (provide (= result (conv_to 64 arg))) + (require (<= (widthof arg) 64))) (decl put_in_reg (Value) Reg) (extern constructor put_in_reg put_in_reg) ;; Put the given value into one or more registers. ;; ;; As a side effect, this marks the value as used. -(spec (put_in_regs arg) (provide (= (conv_to 64 arg) result))) +(spec (put_in_regs arg) + (provide + (= (:lo result) (extract 63 0 (conv_to 128 arg))) + (= (:hi result) (extract 127 64 (conv_to 128 arg))))) (decl put_in_regs (Value) ValueRegs) (extern constructor put_in_regs put_in_regs) @@ -137,8 +154,11 @@ (extern constructor put_in_regs_vec put_in_regs_vec) ;; Get the `n`th register inside a `ValueRegs`. -(spec (value_regs_get arg i) - (provide (= arg result) (= (widthof i) 1))) +(spec (value_regs_get regs index) + (provide + (= result (switch index + (0 (:lo regs)) + (1 (:hi regs)))))) (decl value_regs_get (ValueRegs usize) Reg) (extern constructor value_regs_get value_regs_get) @@ -165,6 +185,7 @@ ;; Convert a MultiReg with three registers into an InstOutput containing ;; one ValueRegs containing the first two regs and one containing the third reg +(attr multi_reg_to_pair_and_single (veri chain)) (decl multi_reg_to_pair_and_single (MultiReg) InstOutput) (rule (multi_reg_to_pair_and_single (MultiReg.Three a b c)) (output_pair (value_regs a b) c)) @@ -245,6 +266,7 @@ (value_list_slice (value_slice_unwrap head1 (value_slice_unwrap head2 tail)))) ;; Turn a `Writable` into a `Reg` via `Writable::to_reg`. +(spec (writable_reg_to_reg reg) (provide (= result reg))) (decl pure writable_reg_to_reg (WritableReg) Reg) (extern constructor writable_reg_to_reg writable_reg_to_reg) @@ -273,13 +295,16 @@ ;; Extract the type of the instruction's first result and pass along the ;; instruction as well. (spec (has_type ty arg) - (provide (= result arg)) - (require (= ty (widthof arg)))) + (provide + (= result arg) + (= (:bits ty) (widthof arg)))) (decl has_type (Type Inst) Inst) (extractor (has_type ty inst) (and (result_type ty) inst)) +(spec (u8_from_iconst x) + (provide (= result (zero_ext 64 x)))) (decl u8_from_iconst (u8) Value) (extractor (u8_from_iconst x) (u64_from_iconst (u8_from_u64 x))) @@ -310,7 +335,16 @@ ;; The value is sign extended to 32 bits. (spec (i32_from_iconst arg) (provide (= arg (extract 31 0 (sign_ext 64 result)))) - (require (= result (sign_ext (widthof result) arg)))) + (match + ; result <= i32::MAX + (bvsle (sign_ext 64 result) #x000000007fffffff) + ; i32::MIN <= result + (bvsle (bvneg #x0000000080000000) (sign_ext 64 result)))) +(instantiate i32_from_iconst + ((args (bv 32)) (ret (bv 8))) + ((args (bv 32)) (ret (bv 16))) + ((args (bv 32)) (ret (bv 32))) + ((args (bv 32)) (ret (bv 64)))) (decl i32_from_iconst (i32) Value) (extractor (i32_from_iconst x) (i64_from_iconst (i32_from_i64 x))) @@ -325,6 +359,7 @@ (extern constructor zero_value zero_value) ;; Match a sinkable instruction from a value operand. +(spec (is_sinkable_inst value) (provide (= result value))) (decl pure partial is_sinkable_inst (Value) Inst) (extern constructor is_sinkable_inst is_sinkable_inst) @@ -335,9 +370,10 @@ ;; Get an unsigned 8-bit immediate in a u8 from an Imm64, if possible. (spec (uimm8 arg) - (provide (= result (zero_ext 64 arg))) - (require (bvslt result #x0000000000000100) - (= (widthof arg) 8))) + (provide (= result (zero_ext 64 arg))) + (match + (bvsge result #x0000000000000000) ; >= 0 + (bvslt result #x0000000000000100))) ; < 256 (decl uimm8 (u8) Imm64) (extern extractor uimm8 uimm8) @@ -372,6 +408,7 @@ (extractor (sge ty x y) (icmp ty (IntCC.SignedGreaterThanOrEqual) x y)) ;; Get the MachLabel for a particular CLIF block's indexed exception-handling successor. +(model Block (type !)) (decl block_exn_successor_label (Block u64) MachLabel) (extern constructor block_exn_successor_label block_exn_successor_label) @@ -381,7 +418,7 @@ ;; ;; This is low-level and side-effectful; it should only be used as an ;; implementation detail by helpers that preserve the SSA facade themselves. - +(spec (emit inst) (provide (= result #b1))) (decl emit (MInst) Unit) (extern constructor emit emit) @@ -390,11 +427,12 @@ ;; This is a side-effectful operation that notifies the context that the ;; instruction has been sunk into another instruction, and no longer needs to ;; be lowered. +(spec (sink_inst inst) (provide true)) (decl sink_inst (Inst) Unit) (extern constructor sink_inst sink_inst) ;; Constant pool emission. - +(model VCodeConstant (type !)) (type VCodeConstant (primitive VCodeConstant)) ;; Add a u64 little-endian constant to the in-memory constant pool and @@ -435,9 +473,8 @@ (inst2 MInst) (inst3 MInst)))) -(model SideEffectNoResult (type Unit)) - ;; Emit given side-effectful instruction. +(spec (emit_side_effect arg) (provide true)) (decl emit_side_effect (SideEffectNoResult) Unit) (rule (emit_side_effect (SideEffectNoResult.Inst inst)) (emit inst)) @@ -452,8 +489,6 @@ ;; Create an empty `InstOutput`, but do emit the given side-effectful ;; instruction. (decl side_effect (SideEffectNoResult) InstOutput) -(spec (side_effect v) - (provide (= result v))) (rule (side_effect inst) (let ((_ Unit (emit_side_effect inst))) (output_none))) @@ -480,6 +515,12 @@ ;; ;; Variant determines how result is given when combined with a ;; ConsumesFlags. See `with_flags` below for more. +(model ProducesFlags + (type + (struct + (flags (named NZCV)) + (result (named Reg)) + (has_result Bool)))) (type ProducesFlags (enum ;; For cases where the flags have been produced by another ;; instruction, and we have out-of-band reasons to know @@ -493,7 +534,31 @@ (ProducesFlagsReturnsReg (inst MInst) (result Reg)) (ProducesFlagsReturnsResultWithConsumer (inst MInst) (result Reg)))) +(spec (ProducesFlags.ProducesFlagsReturnsResultWithConsumer inst reg) + (provide + (= (:flags result) (:flags_out inst)) + (= (:result result) reg) + (:has_result result))) + +(spec (ProducesFlags.ProducesFlagsSideEffect inst) + (provide + (= (:flags result) (:flags_out inst)) + (not (:has_result result)))) + +(spec (ProducesFlags.ProducesFlagsTwiceSideEffect inst1 inst2) + (provide + ; Flags output from the first instruction pass to the second's input. + (= (:flags_out inst1) (:flags_in inst2)) + + ; Result flags of the ProducesFlags wrapper are from the second + ; instruction's output. + (= (:flags result) (:flags_out inst2)) + + ; No value is produced. + (not (:has_result result)))) + ;; Chain another producer to a `ProducesFlags`. +(attr produces_flags_concat (tag chain)) (decl produces_flags_concat (ProducesFlags ProducesFlags) ProducesFlags) (rule (produces_flags_concat (ProducesFlags.ProducesFlagsSideEffect inst1) (ProducesFlags.ProducesFlagsSideEffect inst2)) (ProducesFlags.ProducesFlagsTwiceSideEffect inst1 inst2)) @@ -507,6 +572,12 @@ ;; ;; Variant determines how result is given when combined with a ;; ProducesFlags. See `with_flags` below for more. +(model ConsumesFlags + (type + (struct + (flags (named NZCV)) + (result (named Reg)) + (has_result Bool)))) (type ConsumesFlags (enum (ConsumesFlagsSideEffect (inst MInst)) (ConsumesFlagsSideEffect2 (inst1 MInst) (inst2 MInst)) @@ -521,7 +592,22 @@ (inst4 MInst) (result ValueRegs)))) +(spec (ConsumesFlags.ConsumesFlagsReturnsResultWithProducer inst reg) + (provide + (= (:flags result) (:flags_in inst)) + (= (:result result) reg) + (:has_result result))) +(spec (ConsumesFlags.ConsumesFlagsReturnsReg inst reg) + (provide + (= (:flags result) (:flags_in inst)) + (= (:result result) reg) + (:has_result result))) + +(spec (ConsumesFlags.ConsumesFlagsSideEffect inst) + (provide + (= (:flags result) (:flags_in inst)) + (not (:has_result result)))) ;; Get the produced register out of a ProducesFlags. (decl produces_flags_get_reg (ProducesFlags) Reg) @@ -540,6 +626,18 @@ ;; single Reg, giving a ConsumesFlags that returns both values in a ;; ValueRegs. (decl consumes_flags_concat (ConsumesFlags ConsumesFlags) ConsumesFlags) +;; ConsumesFlags is modeled as a struct with a single `result` Reg. The +;; concatenated form actually carries two registers (a ValueRegs), but +;; downstream verification currently only reasons about the first result +;; produced by `with_flags`, so we record arg `a`'s result as the canonical +;; `result` field. Both inputs share the same incoming flag state, which we +;; propagate to the result. +(spec (consumes_flags_concat a b) + (provide + (= (:flags result) (:flags a)) + (= (:flags a) (:flags b)) + (= (:result result) (:result a)) + (= (:has_result result) (:has_result a)))) (rule (consumes_flags_concat (ConsumesFlags.ConsumesFlagsReturnsReg inst1 reg1) (ConsumesFlags.ConsumesFlagsReturnsReg inst2 reg2)) (ConsumesFlags.ConsumesFlagsTwiceReturnsValueRegs @@ -567,9 +665,26 @@ ;; - ReturnsResultWithProducer + ReturnsResultWithConsumer --> ValueReg with low part from producer, high part from consumer ;; ;; See `with_flags_reg` below for a variant that extracts out just the lower Reg. +(spec (with_flags producer consumer) + (provide + ; Transfer flags from producer to consumer. + (= (:flags producer) (:flags consumer)) + ; Construct resulting value registers. + (=> (and (:has_result producer) (:has_result consumer)) + (and + (= (:lo result) (:result producer)) + (= (:hi result) (:result consumer)) + ) + ) + (=> (and (not (:has_result producer)) (:has_result consumer)) + (= (:lo result) (:result consumer)) + ) + (=> (and (:has_result producer) (not (:has_result consumer))) + (= (:lo result) (:result producer))))) (decl with_flags (ProducesFlags ConsumesFlags) ValueRegs) -(rule (with_flags (ProducesFlags.ProducesFlagsReturnsResultWithConsumer producer_inst producer_result) +(rule with_flags_paired_results + (with_flags (ProducesFlags.ProducesFlagsReturnsResultWithConsumer producer_inst producer_result) (ConsumesFlags.ConsumesFlagsReturnsResultWithProducer consumer_inst consumer_result)) (let ((_x Unit (emit producer_inst)) (_y Unit (emit consumer_inst))) @@ -577,19 +692,22 @@ ;; A flag-producer that also produces a result, paired with a consumer that has ;; no results. -(rule (with_flags (ProducesFlags.ProducesFlagsReturnsResultWithConsumer producer_inst producer_result) +(rule with_flags_producer_result_only + (with_flags (ProducesFlags.ProducesFlagsReturnsResultWithConsumer producer_inst producer_result) (ConsumesFlags.ConsumesFlagsSideEffect consumer_inst)) (let ((_ Unit (emit producer_inst)) (_ Unit (emit consumer_inst))) (value_reg producer_result))) -(rule (with_flags (ProducesFlags.ProducesFlagsSideEffect producer_inst) +(rule with_flags_consumer_reg + (with_flags (ProducesFlags.ProducesFlagsSideEffect producer_inst) (ConsumesFlags.ConsumesFlagsReturnsReg consumer_inst consumer_result)) (let ((_x Unit (emit producer_inst)) (_y Unit (emit consumer_inst))) (value_reg consumer_result))) -(rule (with_flags (ProducesFlags.ProducesFlagsSideEffect producer_inst) +(rule with_flags_consumer_twice + (with_flags (ProducesFlags.ProducesFlagsSideEffect producer_inst) (ConsumesFlags.ConsumesFlagsTwiceReturnsValueRegs consumer_inst_1 consumer_inst_2 consumer_result)) @@ -601,7 +719,8 @@ (_z Unit (emit consumer_inst_2))) consumer_result)) -(rule (with_flags (ProducesFlags.ProducesFlagsSideEffect producer_inst) +(rule with_flags_consumer_four + (with_flags (ProducesFlags.ProducesFlagsSideEffect producer_inst) (ConsumesFlags.ConsumesFlagsFourTimesReturnsValueRegs consumer_inst_1 consumer_inst_2 consumer_inst_3 @@ -617,14 +736,16 @@ (_v Unit (emit consumer_inst_4))) consumer_result)) -(rule (with_flags (ProducesFlags.ProducesFlagsTwiceSideEffect producer_inst1 producer_inst2) +(rule with_flags_producer_twice_reg + (with_flags (ProducesFlags.ProducesFlagsTwiceSideEffect producer_inst1 producer_inst2) (ConsumesFlags.ConsumesFlagsReturnsReg consumer_inst consumer_result)) (let ((_ Unit (emit producer_inst1)) (_ Unit (emit producer_inst2)) (_ Unit (emit consumer_inst))) (value_reg consumer_result))) -(rule (with_flags (ProducesFlags.ProducesFlagsTwiceSideEffect producer_inst1 producer_inst2) +(rule with_flags_producer_twice_consumer_twice + (with_flags (ProducesFlags.ProducesFlagsTwiceSideEffect producer_inst1 producer_inst2) (ConsumesFlags.ConsumesFlagsTwiceReturnsValueRegs consumer_inst_1 consumer_inst_2 consumer_result)) @@ -637,7 +758,8 @@ (_ Unit (emit consumer_inst_2))) consumer_result)) -(rule (with_flags (ProducesFlags.ProducesFlagsTwiceSideEffect producer_inst1 producer_inst2) +(rule with_flags_producer_twice_consumer_four + (with_flags (ProducesFlags.ProducesFlagsTwiceSideEffect producer_inst1 producer_inst2) (ConsumesFlags.ConsumesFlagsFourTimesReturnsValueRegs consumer_inst_1 consumer_inst_2 consumer_inst_3 @@ -1061,11 +1183,6 @@ (type RealReg (primitive RealReg)) ;; Instruction on whether and how to extend an argument value. -(model ArgumentExtension - (enum - (None) - (Uext) - (Sext))) (type ArgumentExtension extern (enum (None) @@ -1173,10 +1290,20 @@ (extern constructor try_call_none try_call_none) ;; Helper for extracting an immediate that's not 0 and not -1 from an imm64. - (spec (safe_divisor_from_imm64 t x) - (provide (= (sign_ext 64 x) result)) - (require (not (= #x0000000000000000 result)) - (not (= #x1111111111111111 result)))) +(spec (safe_divisor_from_imm64 ty arg) + (match + (let ( + (minus1 (zero_ext 64 (bvones! (:bits ty)))) + (zero (bvzero! 64)) + (bits (bvand arg minus1))) + (and + (not (= bits minus1)) + (not (= bits zero))))) + (provide + (let ( + (minus1 (zero_ext 64 (bvones! (:bits ty)))) + (bits (bvand arg minus1))) + (= result bits)))) (decl pure partial safe_divisor_from_imm64 (Type Imm64) u64) (extern constructor safe_divisor_from_imm64 safe_divisor_from_imm64) @@ -1195,3 +1322,10 @@ (convert ValueRegsVec InstOutput output_vec) (convert ExternalName BoxExternalName box_external_name) (convert PReg Reg preg_to_reg) + +;;;; Additional verification terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(model Inst (type (bv))) +(model Constant (type (bv))) +(spec (def_inst arg) + (provide (= result arg))) +(model StackSlot (type !)) diff --git a/cranelift/codegen/src/spec/fpconst.isle b/cranelift/codegen/src/spec/fpconst.isle new file mode 100644 index 000000000000..cbfee40b078a --- /dev/null +++ b/cranelift/codegen/src/spec/fpconst.isle @@ -0,0 +1,55 @@ +; Build one as a floating-point of the given width. +(macro (fp_one w) + (conv_to w + (switch w + (32 #x000000003f800000) + (64 #x3ff0000000000000)))) + +; Build negative one as a floating-point of the given width. +(macro (fp_minus_one w) + (conv_to w + (switch w + (32 #x00000000bf800000) + (64 #xbff0000000000000)))) + +; Build half as a floating-point of the given width. +(macro (fp_half w) + (conv_to w + (switch w + (32 #x000000003f000000) + (64 #x3fe0000000000000)))) + +; Build negative half as a floating-point of the given width. +(macro (fp_minus_half w) + (conv_to w + (switch w + (32 #x00000000bf000000) + (64 #xbfe0000000000000)))) + +; Build 32-bit integer minimum as a floating-point of the given width. +(macro (fp_i32_min w) + (conv_to w + (switch w + (32 #x00000000cf000000) + (64 #xc1e0000000000000)))) + +; Build negative 32-bit integer minimum as a floating-point of the given width. +(macro (fp_minus_i32_min w) + (conv_to w + (switch w + (32 #x000000004f000000) + (64 #x41e0000000000000)))) + +; Build 64-bit integer minimum as a floating-point of the given width. +(macro (fp_i64_min w) + (conv_to w + (switch w + (32 #x00000000df000000) + (64 #xc3e0000000000000)))) + +; Build negative 64-bit integer minimum as a floating-point of the given width. +(macro (fp_minus_i64_min w) + (conv_to w + (switch w + (32 #x000000005f000000) + (64 #x43e0000000000000)))) diff --git a/cranelift/codegen/src/spec/inst_specs.isle b/cranelift/codegen/src/spec/inst_specs.isle new file mode 100644 index 000000000000..c28d1ccd4f65 --- /dev/null +++ b/cranelift/codegen/src/spec/inst_specs.isle @@ -0,0 +1,1290 @@ +;;;; Verification Type Models ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(model Imm64 (type (bv 64))) + +(model Ieee32 (type (bv 32))) +(model Ieee64 (type (bv 64))) + +(model MemFlags (type + (struct + (aligned Bool) + (trapcode (bv 4))))) + +(model MemFlagsData (type + (struct + (aligned Bool) + (trapcode (bv 4))))) + +(model Offset32 (type (bv 32))) + +;;;; State Definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Value loaded from memory. +; +; We deliberately do not attempt to model the entire state of memory. Modeling a +; loaded value this way allows us to express the fact that loaded values on CLIF +; and ISA side will be equivalent (combined with an assertion on address +; equality). +(state loaded_value + (type (bv 64)) + (default true)) + +; Parameters of a CLIF load operation. +(state clif_load + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)))) + (default + (not (:active clif_load)))) + +; Parameters of a CLIF store operation. +(state clif_store + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)) + (value (bv 64)))) + (default + (and + ; Store is not active. + (not (:active clif_store)) + + ; Must provide a fixed size in the default case, otherwise type + ; inference is underconstrained. + (= (:size_bits clif_store) 1)))) + +; Whether a trap is expected according to CLIF semantics. +(state clif_trap + (type Bool) + (default (not clif_trap))) + +;;;; Common Term Forms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(form + bv_unary_8_to_64 + ((args (named Type) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +(form + bv_binary_8_to_64 + ((args (named Type) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + +(form + bv_ternary_8_to_64 + ((args (named Type) (bv 8) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64) (bv 64)) (ret (bv 64)))) + +;;;; CLIF Instruction Specifications ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Integer Instructions + +(spec (iadd ty x y) + (provide (= result (bvadd x y)) + (= (:bits ty) (widthof result)))) +(instantiate iadd + ((args (named Type) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) + ((args (named Type) (bv 128) (bv 128)) (ret (bv 128)))) + +(spec (isub ty x y) + (provide (= result (bvsub x y)) + (= (:bits ty) (widthof result)))) +(instantiate isub bv_binary_8_to_64) + +(spec (ineg ty x) + (provide (= result (bvneg x)) + (= (:bits ty) (widthof result)))) +(instantiate ineg bv_unary_8_to_64) + +(spec (iabs ty x) + (provide (= result (if (bvsge x (zero_ext (widthof x) #b0)) x (bvneg x))) + (= (:bits ty) (widthof result)))) +(instantiate iabs bv_unary_8_to_64) + +(spec (imul ty x y) + (provide (= result (bvmul x y)) + (= (:bits ty) (widthof result)))) +(instantiate imul bv_binary_8_to_64) + +(spec (smulhi ty x y) + (provide + (= (:bits ty) (widthof result)) + (let + ( + (double (concat x x)) + (double_width (widthof double)) + (xwide (sign_ext double_width x)) + (ywide (sign_ext double_width y))) + (with (low) + (= (concat result low) (bvmul xwide ywide)))))) +(instantiate smulhi bv_binary_8_to_64) + +(spec (umulhi ty x y) + (provide + (= (:bits ty) (widthof result)) + (let + ( + (double (concat x x)) + (double_width (widthof double)) + (xwide (zero_ext double_width x)) + (ywide (zero_ext double_width y))) + (with (low) + (= (concat result low) (bvmul xwide ywide)))))) +(instantiate umulhi bv_binary_8_to_64) + +(spec (udiv ty x y) + (modifies clif_trap) + (provide + (= result (bvudiv x y)) + (= clif_trap (bv_is_zero! y)) + (= (:bits ty) (widthof result)))) +(instantiate udiv bv_binary_8_to_64) + +(spec (sdiv ty x y) + (modifies clif_trap) + (provide + ; Constrain the result width in all cases + (= (widthof result) (:bits ty)) + ; If j2 is 0, then the result is undefined. + (if (bv_is_zero! y) + clif_trap + ; Else if j1 divided by j2 is 2^{N−1}, then the result is undefined. + ; + ; Note: the only way this can happen is the case (−2^{N−1})/(−1). + (if (and + ; x is -2^{N-1} + (= x (bv_top_bit_set! (widthof x))) + ; y is -1 + (bv_is_zero! (bvnot y))) + clif_trap + ; Else, return the result of dividing j1 by j2, truncated toward zero. + (and + (not clif_trap) + (= result (bvsdiv x y))))))) + +(instantiate sdiv bv_binary_8_to_64) +(spec (urem ty x y) + (modifies clif_trap) + (provide + (= (:bits ty) (widthof result)) + ; If i2 is 0, then the result is undefined. + (if (bv_is_zero! y) + clif_trap + ; Else, return the remainder of dividing i1 by i2. + (and + (not clif_trap) + (= result (bvurem x y)))))) +(instantiate urem bv_binary_8_to_64) + +(spec (srem ty x y) + (modifies clif_trap) + (provide + (= (:bits ty) (widthof result)) + ; Let j1 be the signed interpretation of i1. + ; Let j2 be the signed interpretation of i2. + ; If i2 is 0, then the result is undefined. + (if (bv_is_zero! y) + clif_trap + ; Else, return the remainder of dividing j1 by j2, with the sign of the dividend j1. + (and + (not clif_trap) + (= result (bvsrem x y)))))) +(instantiate srem bv_binary_8_to_64) + +;; "Unsigned addition of x and y, trapping if the result overflows." +;; "Accepts 32 or 64-bit integers, and does not support vector types." +(spec (uadd_overflow_trap ty x y trap_code) + (modifies clif_trap) + (provide + (= (:bits ty) (widthof result)) + (let + ( + (N (widthof x)) + ;; Use at least 1 extra bit for unsigned overflow + (sum (bvadd (zero_ext 65 x) (zero_ext 65 y))) + ;; Unsigned overflow if some carry out. + (carry + (switch N + (32 (extract 32 32 sum)) + (64 (extract 64 64 sum))))) + (if (= carry #b1) + clif_trap + (and + (not clif_trap) + (= result (conv_to N sum))))))) + +(instantiate uadd_overflow_trap + ((args (named Type) (bv 32) (bv 32) (named TrapCode)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64) (named TrapCode)) (ret (bv 64)))) + +(spec (trap trap_code) + (modifies clif_trap) + (provide clif_trap)) + +(spec (iconst ty arg) + (provide (= arg (zero_ext 64 result)) + (= (:bits ty) (widthof result)))) +(instantiate iconst + ((args (named Type) (bv 64)) (ret (bv 8))) + ((args (named Type) (bv 64)) (ret (bv 16))) + ((args (named Type) (bv 64)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +(spec (ishl ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + (bvshl + x + (bvand y (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) #x0000000000000001))))))) +(instantiate ishl bv_binary_8_to_64) + +(spec (ushr ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + (bvlshr + x + (bvand y (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) #x0000000000000001))))))) +(instantiate ushr bv_binary_8_to_64) + +(spec (sshr ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + (bvashr + x + (bvand y (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) #x0000000000000001))))))) +(instantiate sshr bv_binary_8_to_64) + +(spec (band ty x y) + (provide (= result (bvand x y)) + (= (:bits ty) (widthof result)))) +(instantiate band bv_binary_8_to_64) + +(spec (bxor ty x y) + (provide (= result (bvxor x y)) + (= (:bits ty) (widthof result)))) +(instantiate bxor bv_binary_8_to_64) + +(spec (bor ty x y) + (provide (= result (bvor x y)) + (= (:bits ty) (widthof result)))) +(instantiate bor bv_binary_8_to_64) + +(spec (bnot ty x) + (provide (= result (bvnot x)) + (= (:bits ty) (widthof result)))) +(instantiate bnot bv_unary_8_to_64) + +(spec (rotl ty x y) + (provide (= result (rotl x y)) + (= (:bits ty) (widthof result)))) +(instantiate rotl bv_binary_8_to_64) + +(spec (rotr ty x y) + (provide (= result (rotr x y)) + (= (:bits ty) (widthof result)))) +(instantiate rotr bv_binary_8_to_64) + +(spec (bitselect ty c x y) + (provide (= result (bvor (bvand c x) (bvand (bvnot c) y))) + (= (:bits ty) (widthof result)))) +(instantiate bitselect bv_ternary_8_to_64) + +(spec (cls ty x) + (provide (= result (cls x)) + (= (:bits ty) (widthof result)))) +(instantiate cls bv_unary_8_to_64) + +(spec (clz ty x) + (provide (= result (clz x)) + (= (:bits ty) (widthof result)))) +(instantiate clz bv_unary_8_to_64) + +(spec (ctz ty x) + (provide (= result (clz (rev x))) + (= (:bits ty) (widthof result)))) +(instantiate ctz bv_unary_8_to_64) + +(spec (popcnt ty x) + (provide (= result (popcnt x)) + (= (:bits ty) (widthof result)))) +(instantiate popcnt bv_unary_8_to_64) + +;; Reverse the byte order of `x`. There is no byte-swap primitive, so we build +;; the result by concatenating the bytes of `x` in reverse order (`concat`'s +;; first argument is the high-order part). `bswap` is defined for i16/i32/i64/ +;; i128; we verify the 16/32/64-bit widths, matching the rest of this file. +(spec (bswap ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + (switch (widthof x) + (16 (concat (extract 7 0 x) (extract 15 8 x))) + (32 (concat (extract 7 0 x) + (concat (extract 15 8 x) + (concat (extract 23 16 x) + (extract 31 24 x))))) + (64 (concat (extract 7 0 x) + (concat (extract 15 8 x) + (concat (extract 23 16 x) + (concat (extract 31 24 x) + (concat (extract 39 32 x) + (concat (extract 47 40 x) + (concat (extract 55 48 x) + (extract 63 56 x))))))))))))) +(instantiate bswap + ((args (named Type) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +(spec (ireduce ty x) + (provide (= result (conv_to (widthof result) x)) + (= (:bits ty) (widthof result)))) +(instantiate ireduce + ((args (named Type) (bv 16)) (ret (bv 8))) + ((args (named Type) (bv 32)) (ret (bv 8))) + ((args (named Type) (bv 64)) (ret (bv 8))) + ((args (named Type) (bv 32)) (ret (bv 16))) + ((args (named Type) (bv 64)) (ret (bv 16))) + ((args (named Type) (bv 64)) (ret (bv 32)))) + +(form extend + ((args (named Type) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 8)) (ret (bv 16))) + ((args (named Type) (bv 8)) (ret (bv 32))) + ((args (named Type) (bv 8)) (ret (bv 64))) + ((args (named Type) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 16)) (ret (bv 32))) + ((args (named Type) (bv 16)) (ret (bv 64))) + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 32)) (ret (bv 64)))) + ;; Note: (bv 64) -> (bv 64) not accepted in clif + +(spec (uextend ty x) + (provide (= result (zero_ext (widthof result) x)) + (= (:bits ty) (widthof result)))) +(instantiate uextend extend) + +(spec (sextend ty x) + (provide (= result (sign_ext (widthof result) x)) + (= (:bits ty) (widthof result)))) +(instantiate sextend extend) + +;; `maybe_uextend` "sees through" a `uextend`: given the outer value `result`, +;; it yields the inner value `value`. When `result` is defined by a `uextend`, +;; `value` is that uextend's argument; otherwise `value` is `result` itself. In +;; both cases the inner value is the low bits of the outer value, i.e. the outer +;; value is the zero-extension of the inner one to the outer width (in the +;; fall-through case the two widths are equal, so this is the identity). The +;; extractor is total (it always matches), so no `match` clause is needed. +(spec (maybe_uextend value) + (provide (= result (zero_ext (widthof result) value)))) + +(spec (smin ty x y) + (provide (= result (if (bvsle x y) x y)) + (= (:bits ty) (widthof result)))) +(instantiate smin bv_binary_8_to_64) + +(spec (umin ty x y) + (provide (= result (if (bvule x y) x y)) + (= (:bits ty) (widthof result)))) +(instantiate umin bv_binary_8_to_64) + +(spec (smax ty x y) + (provide (= result (if (bvsge x y) x y)) + (= (:bits ty) (widthof result)))) +(instantiate smax bv_binary_8_to_64) + +(spec (umax ty x y) + (provide (= result (if (bvuge x y) x y)) + (= (:bits ty) (widthof result)))) +(instantiate umax bv_binary_8_to_64) + +(spec (icmp ty cc x y) + (provide + (= result + (if + (match cc + ((Equal) (= x y)) + ((NotEqual) (not (= x y))) + ((SignedGreaterThan) (bvsgt x y)) + ((SignedGreaterThanOrEqual) (bvsge x y)) + ((SignedLessThan) (bvslt x y)) + ((SignedLessThanOrEqual) (bvsle x y)) + ((UnsignedGreaterThan) (bvugt x y)) + ((UnsignedGreaterThanOrEqual) (bvuge x y)) + ((UnsignedLessThan) (bvult x y)) + ((UnsignedLessThanOrEqual) (bvule x y))) + #x01 + #x00)))) +(instantiate icmp + ((args (named Type) (named IntCC) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (named IntCC) (bv 16) (bv 16)) (ret (bv 8))) + ((args (named Type) (named IntCC) (bv 32) (bv 32)) (ret (bv 8))) + ((args (named Type) (named IntCC) (bv 64) (bv 64)) (ret (bv 8)))) + +;; Load Instructions + +; Compute the effective address of a base pointer p and fixed offset. +(macro (effective_address p offset) + (bvadd p (sign_ext 64 offset))) + +; Activate and set parameters of a CLIF load effect. +(macro (clif_load_activate clif_load size_bits p offset) + (and + (:active clif_load) + (= (:size_bits clif_load) size_bits) + (= (:addr clif_load) (effective_address! p offset)))) + +; Load from memory +(spec (load ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide + (= (:bits ty) (widthof result)) + + ; Activate the CLIF load effect. + (clif_load_activate! clif_load (widthof result) p offset) + + ; Result of the load is represented by low bits of the loaded value state register. + (= result (conv_to (widthof result) loaded_value)))) +(instantiate load + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 8))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 16))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 64)))) + +; Unsigned N-bit load +(macro (uloadN clif_load size_bits p offset loaded_value result) + (and + ; Activate the CLIF load effect. + (clif_load_activate! clif_load size_bits p offset) + + ; Loaded value is zero-extended. + (= result (zero_ext (widthof result) (conv_to size_bits loaded_value))))) + +; Unsigned 8-bit load +(spec (uload8 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (uloadN! clif_load 8 p offset loaded_value result) + (= (:bits ty) (widthof result)))) +(instantiate uload8 + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 16))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 64)))) + +; Unsigned 16-bit load +(spec (uload16 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (uloadN! clif_load 16 p offset loaded_value result) + (= (:bits ty) (widthof result)))) +(instantiate uload16 + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 64)))) + +; Unsigned 32-bit load +(spec (uload32 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (uloadN! clif_load 32 p offset loaded_value result) + (= (:bits ty) (widthof result)))) +(instantiate uload32 + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 64)))) + +; Signed N-bit load +(macro (sloadN clif_load size_bits p offset loaded_value result) + (and + ; Activate the CLIF load effect. + (clif_load_activate! clif_load size_bits p offset) + + ; Loaded value is sign-extended. + (= result (sign_ext (widthof result) (conv_to size_bits loaded_value))))) + +; Signed 8-bit load +(spec (sload8 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (sloadN! clif_load 8 p offset loaded_value result) + (= (:bits ty) (widthof result)))) +(instantiate sload8 + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 16))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 64)))) + +; Signed 16-bit load +(spec (sload16 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (sloadN! clif_load 16 p offset loaded_value result) + (= (:bits ty) (widthof result)))) +(instantiate sload16 + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 64)))) + +; Signed 32-bit load +(spec (sload32 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (sloadN! clif_load 32 p offset loaded_value result) + (= (:bits ty) (widthof result)))) +(instantiate sload32 + ((args (named Type) (named MemFlagsData) (named Value) (named Offset32)) (ret (bv 64)))) + +; Loads have a large number of expansions and instantiations. +(attr load (tag slow)) +(attr uload8 (tag slow)) +(attr uload16 (tag slow)) +(attr uload32 (tag slow)) +(attr sload8 (tag slow)) +(attr sload16 (tag slow)) +(attr sload32 (tag slow)) + +;; Store Instructions + +; Activate and set parameters of a CLIF store effect. +(macro (clif_store_activate clif_store value p offset) + (and + ; Activate the CLIF store effect + (:active clif_store) + + ; Store size is the width of the stored value. + (= (:size_bits clif_store) (widthof value)) + + ; Address calculation. + (= (:addr clif_store) (effective_address! p offset)) + + ; Stored value is set to the low bits of the CLIF store value. + (= (conv_to (widthof value) (:value clif_store)) value))) + +; Store instruction specification. +(macro (store clif_store flags value p offset result) + (and + ; Activate the CLIF store effect + (clif_store_activate! clif_store value p offset) + + ; HACK: Result of the store is a 1-bit vector. + (= result #b1))) + +; Store to memory +(spec (store flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags value p offset result))) +(instantiate store + ((args (named MemFlagsData) (bv 8) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlagsData) (bv 16) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlagsData) (bv 32) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlagsData) (bv 64) (named Value) (named Offset32)) (ret (bv 1)))) + +; 8-bit store +(spec (istore8 flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags (extract 7 0 value) p offset result))) +(instantiate istore8 + ((args (named MemFlagsData) (bv 16) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlagsData) (bv 32) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlagsData) (bv 64) (named Value) (named Offset32)) (ret (bv 1)))) + +; 16-bit store +(spec (istore16 flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags (extract 15 0 value) p offset result))) +(instantiate istore16 + ((args (named MemFlagsData) (bv 32) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlagsData) (bv 64) (named Value) (named Offset32)) (ret (bv 1)))) + +; 32-bit store +(spec (istore32 flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags (extract 31 0 value) p offset result))) +(instantiate istore32 + ((args (named MemFlagsData) (bv 64) (named Value) (named Offset32)) (ret (bv 1)))) + +; Stores have a large number of expansions and instantiations. +(attr store (tag slow)) +(attr istore8 (tag slow)) +(attr istore16 (tag slow)) +(attr istore32 (tag slow)) + +;; Floating Point Instructions + +; NaN Propagation: see WebAssembly Specification 2.0, section 4.3.3 + +; Evaluates the positive WebAssembly canonical NaN of the given width. +(macro (nan_canon w) + (conv_to w + (switch w + (32 #x000000007fc00000) + (64 #x7ff8000000000000)))) + +; NaN propagation with zero inputs. +; +; The CLIF semantics (inherited from WebAssembly) only requires a NaN payload +; with the top bit set. Our specification is a refinement, selecting the +; positive canonical NaN. +(macro (nans0 w) (nan_canon! w)) + +; NaN propagation with one input. +; +; The CLIF semantics (inherited from WebAssembly) requires that a canonical NaN +; input is preserved, while any other NaN is mapped to any arithmetic NaN (which +; has the top fraction bit set). Our chosen refinement is to return the NaN +; input with the top fraction bit or-ed in: this both preserves the canonical +; NaN and turns any other NaN into an arithmetic NaN. +(macro (nans1 x) (if (fp.isNaN x) (bvor x (fp_topfrac_bit_set! (widthof x))) (nans0! (widthof x)))) + +; NaN propagation with two inputs. +; +; The CLIF semantics (inherited from WebAssembly) requires that if both inputs +; are canonical then the output must be. Otherwise the output must be an +; arithmetic NaN. Our chosen refinement is to apply single-input NaN propagation +; to the first input if it's a NaN, otherwise to the second input if it's a NaN, +; and fallback to returning the canonical NaN. +(macro (nans2 x y) (if (fp.isNaN x) (nans1! x) (if (fp.isNaN y) (nans1! y) (nans0! (widthof x))))) + +; NaN negation. +; +; The CLIF semantics (inherited from WebAssembly) requires that negating a NaN +; flips the sign bit (rather than returning a nondeterministic NaN). +(macro (nan_neg x) + (conv_to (widthof x) + (bvxor x (fp_sign_bit_set! (widthof x))))) + +; f32const: single-precision floating-point constant. +(spec (f32const ty x) + (provide (= result x) + (= (:bits ty) (widthof result)))) +(instantiate f32const ((args (named Type) (bv 32)) (ret (bv 32)))) + +; f64const: double-precision floating-point constant. +(spec (f64const ty x) + (provide (= result x) + (= (:bits ty) (widthof result)))) +(instantiate f64const ((args (named Type) (bv 64)) (ret (bv 64)))) + +; fcmp: floating-point compare. +(spec (fcmp ty c x y) + (provide + ;; Restrict to operations used from Wasm for now + (or (= c (FloatCC.Equal)) + (= c (FloatCC.NotEqual)) + (= c (FloatCC.LessThan)) + (= c (FloatCC.GreaterThan)) + (= c (FloatCC.LessThanOrEqual)) + (= c (FloatCC.GreaterThanOrEqual))) + (= result + (if + (match c + ((Equal) (fp.eq x y)) + ((NotEqual) (fp.ne x y)) + ((LessThan) (fp.lt x y)) + ((GreaterThan) (fp.gt x y)) + ((LessThanOrEqual) (fp.le x y)) + ((GreaterThanOrEqual) (fp.ge x y))) + #x01 + #x00)))) +(instantiate fcmp + ((args (named Type) (named FloatCC) (bv 32) (bv 32)) (ret (bv 8))) + ((args (named Type) (named FloatCC) (bv 64) (bv 64)) (ret (bv 8)))) + +; fadd: floating-point addition. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fadd ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if both z1 and z2 are infinities of opposite signs, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_opposite_sign! x y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are infinities of equal sign, then return that infinity. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_equal_sign! x y)) + x + ; Else if either z1 or z2 is an infinity, then return that infinity. + (if (fp.isInfinite x) + x + (if (fp.isInfinite y) + y + ; Else if both z1 and z2 are zeroes of opposite sign, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.+zero (widthof x)) + ; Else if both z1 and z2 are zeroes of equal sign, then return that zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_equal_sign! x y)) + x + ; Else if either z1 or z2 is a zero, then return the other operand. + (if (fp.isZero x) + y + (if (fp.isZero y) + x + ; Else if both z1 and z2 are values with the same magnitude but opposite signs, then return positive zero. + (if (and (= (fp_magnitude! x) (fp_magnitude! y)) (fp_opposite_sign! x y)) + (fp.+zero (widthof x)) + ; Else return the result of adding z1 and z2, rounded to the nearest representable value. + (fp.add x y)))))))))))))) +(instantiate fadd + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + +; fsub: floating-point subtraction. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fsub ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if both z1 and z2 are infinities of equal sign, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_equal_sign! x y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are infinities of opposite signs, then return z1. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_opposite_sign! x y)) + x + ; Else if z1 is an infinity, then return that infinity. + (if (fp.isInfinite x) + x + ; Else if z2 is an infinity, then return that infinity negated. + (if (fp.isInfinite y) + (fp.neg y) + ; Else if both z1 and z2 are zeroes of equal sign, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if both z1 and z2 are zeroes of opposite sign, then return z1. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + x + ; Else if z2 is a zero, then return z1. + (if (fp.isZero y) + x + ; Else if z1 is a zero, then return z2 negated. + (if (fp.isZero x) + (fp.neg y) + ; Else if both z1 and z2 are the same value, then return positive zero. + (if (and (= (fp_magnitude! x) (fp_magnitude! y)) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else return the result of subtracting z2 from z1, rounded to the nearest representable value. + (fp.sub x y)))))))))))))) +(instantiate fsub + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + +; fmul: floating-point multiplication. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fmul ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if one of z1 and z2 is a zero and the other an infinity, then return an element of nans{}. + (if (and (fp.isZero x) (fp.isInfinite y)) + (nans0! (widthof x)) + (if (and (fp.isInfinite x) (fp.isZero y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are infinities of equal sign, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if both z1 and z2 are infinities of opposite signs, then return z1. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else if either z1 or z2 is an infinity and the other a value with equal sign, then return positive infinity. + (if (and (fp.isInfinite x) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + (if (and (fp.isInfinite y) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if either z1 or z2 is an infinity and the other a value with opposite sign, then return negative infinity. + (if (and (fp.isInfinite x) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + (if (and (fp.isInfinite y) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else if both z1 and z2 are zeroes of equal sign, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if both z1 and z2 are zeroes of opposite sign, then return negative zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else return the result of multiplying z1 and z2, rounded to the nearest representable value. + (fp.mul x y))))))))))))))) +(instantiate fmul + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + +; fdiv: floating-point division. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fdiv ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if both z1 and z2 are infinities, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are zeroes, then return an element of nans{z1,z2}. + (if (and (fp.isZero x) (fp.isZero y)) + (nans2! x y) + ; Else if z1 is an infinity and z2 a value with equal sign, then return positive infinity. + (if (and (fp.isInfinite x) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if z1 is an infinity and z2 a value with opposite sign, then return negative infinity. + (if (and (fp.isInfinite x) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else if z2 is an infinity and z1 a value with equal sign, then return positive zero. + (if (and (fp.isInfinite y) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if z2 is an infinity and z1 a value with opposite sign, then return negative zero. + (if (and (fp.isInfinite y) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else if z1 is a zero and z2 a value with equal sign, then return positive zero. + (if (and (fp.isZero x) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if z1 is a zero and z2 a value with opposite sign, then return negative zero. + (if (and (fp.isZero x) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else if z2 is a zero and z1 a value with equal sign, then return positive infinity. + (if (and (fp.isZero y) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if z2 is a zero and z1 a value with opposite sign, then return negative infinity. + (if (and (fp.isZero y) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else return the result of dividing z1 by z2, rounded to the nearest representable value. + (fp.div x y))))))))))))))) +(instantiate fdiv + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + + +; fmin: floating-point minimum. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fmin ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if either z1 or z2 is a negative infinity, then return negative infinity. + (if (or (and (fp.isInfinite x) (fp.isNegative x)) (and (fp.isInfinite y) (fp.isNegative y))) + (fp.-oo (widthof x)) + ; Else if either z1 or z2 is a positive infinity, then return the other value. + (if (and (fp.isInfinite x) (fp.isPositive x)) + y + (if (and (fp.isInfinite y) (fp.isPositive y)) + x + ; Else if both z1 and z2 are zeroes of opposite signs, then return negative zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else return the smaller value of z1 and z2. + (fp.min x y))))))))) +(instantiate fmin + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + +; fmax: floating-point minimum. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fmax ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if either z1 or z2 is a positive infinity, then return positive infinity. + (if (or (and (fp.isInfinite x) (fp.isPositive x)) (and (fp.isInfinite y) (fp.isPositive y))) + (fp.+oo (widthof x)) + ; Else if either z1 or z2 is a negative infinity, then return the other value. + (if (and (fp.isInfinite x) (fp.isNegative x)) + y + (if (and (fp.isInfinite y) (fp.isNegative y)) + x + ; Else if both z1 and z2 are zeroes of opposite signs, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.+zero (widthof x)) + ; Else return the smaller value of z1 and z2. + (fp.max x y))))))))) +(instantiate fmax + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + + +; fabs: floating-point absolute value. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fabs ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a NaN, then return z with positive sign. + (if (fp.isNaN x) + (bvand x (bvnot (fp_sign_bit_set! (widthof x)))) + ; Else if z is an infinity, then return positive infinity. + (if (fp.isInfinite x) + (fp.+oo (widthof x)) + ; Else if z is a zero, then return positive zero. + (if (fp.isZero x) + (fp.+zero (widthof x)) + ; Else if z is a positive value, then return z. + (if (fp.isPositive x) + x + ; Else return z negated. + (fp.neg x)))))))) +(instantiate fabs + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + + +; fneg: floating-point negation. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fneg ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a NaN, then return z with negated sign. + (if (fp.isNaN x) + (nan_neg! x) + ; Else if z is an infinity, then return that infinity negated. + ; Else if z is a zero, then return that zero negated. + ; Else return z negated. + (fp.neg x))))) ; Remaining cases of the spec handled by SMT fp.neg +(instantiate fneg + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + + +; sqrt: floating-point square root. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (sqrt ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is negative infinity, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isNegative x)) + (nans0! (widthof x)) + ; Else if z is positive infinity, then return positive infinity. + (if (and (fp.isInfinite x) (fp.isPositive x)) + (fp.+oo (widthof x)) + ; Else if z is a zero, then return that zero. + (if (fp.isZero x) + x + ; Else if z has a negative sign, then return an element of nans{}. + (if (fp.isNegative x) + (nans0! (widthof x)) + ; Else return the square root of z. + (fp.sqrt x))))))))) +(instantiate sqrt + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +; ceil: floating-point ceiling. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (ceil ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is zero, then return z. + (if (fp.isZero x) + x + ; Else if z is smaller than 0 but greater than −1, then return negative zero. + (if (and (fp.lt x (fp.-zero (widthof x))) (fp.gt x (fp_minus_one! (widthof x)))) + (fp.-zero (widthof x)) + ; Else return the smallest integral value that is not smaller than z. + (fp.ceil x)))))))) +(instantiate ceil + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +; floor: floating-point floor. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (floor ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is zero, then return z. + (if (fp.isZero x) + x + ; Else if z is greater than 0 but smaller than 1, then return positive zero. + (if (and (fp.gt x (fp.-zero (widthof x))) (fp.lt x (fp_one! (widthof x)))) + (fp.+zero (widthof x)) + ; Else return the smallest integral value that is not smaller than z. + (fp.floor x)))))))) +(instantiate floor + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +; trunc: floating-point truncate. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (trunc ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is a zero, then return z. + (if (fp.isZero x) + x + ; Else if z is greater than 0 but smaller than 1, then return positive zero. + (if (and (fp.gt x (fp.+zero (widthof x))) (fp.lt x (fp_one! (widthof x)))) + (fp.+zero (widthof x)) + ; Else if z is smaller than 0 but greater than −1, then return negative zero. + (if (and (fp.lt x (fp.-zero (widthof x))) (fp.gt x (fp_minus_one! (widthof x)))) + (fp.-zero (widthof x)) + ; Else return the integral value with the same sign as z and the largest magnitude that is not larger than the magnitude of z. + (fp.trunc x))))))))) +(instantiate trunc + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) +; nearest: floating-point nearest. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (nearest ty x) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is zero, then return z. + (if (fp.isZero x) + x + ; Else if z is greater than 0 but smaller than or equal to 0.5, then return positive zero. + (if (and (fp.gt x (fp.+zero (widthof x))) (fp.le x (fp_half! (widthof x)))) + (fp.+zero (widthof x)) + ; Else if z is smaller than 0 but greater than or equal to −0.5, then return negative zero. + (if (and (fp.lt x (fp.-zero (widthof x))) (fp.ge x (fp_minus_half! (widthof x)))) + (fp.-zero (widthof x)) + ; Else return the integral value that is nearest to z; if two values are equally near, return the even one. + (fp.nearest x))))))))) +(instantiate nearest + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +; fcopysign: floating-point copysign. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fcopysign ty x y) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z1 and z2 have the same sign, then return z1. + (if (fp_equal_sign_inc_nan! x y) + x + ; Else return z1 with negated sign. + (if (fp.isNaN x) + (nan_neg! x) + (fp.neg x)))))) +(instantiate fcopysign + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) + +(spec (bitcast ty flags x) + (provide (= result x) + (= (:bits ty) (widthof result)))) +; I32ReinterpretF32 +; I64ReinterpretF64 +; F32ReinterpretI32 +; F64ReinterpretI64 +(instantiate bitcast + ((args (named Type) (named MemFlagsData) (bv 32)) (ret (bv 32))) + ((args (named Type) (named MemFlagsData) (bv 64)) (ret (bv 64)))) + +(form fcvt + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 32)) (ret (bv 64))) + ((args (named Type) (bv 64)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +(spec (fcvt_from_uint ty x) + (provide + (= (:bits ty) (widthof result)) + (let ((N (widthof result))) + (= result (to_fp_unsigned N (conv_to N (zero_ext 64 x))))))) +(instantiate fcvt_from_uint fcvt) + +(spec (fcvt_from_sint ty x) + (provide + (= (:bits ty) (widthof result)) + (let ((N (widthof result))) + (= result (to_fp N (conv_to N (sign_ext 64 x))))))) +(instantiate fcvt_from_sint fcvt) + +;; Can trap if invalid conversion +;; Derived from Wasm reference interpreter +;; https://github.com/WebAssembly/spec/blob/5d12bd74c49932deb7ab4bae3d29bf106f19d10b/interpreter/exec/i32_convert.ml#L5 +(macro (neg_min_int_times_two_as_fp w) + (switch w + (32 #x000000004f800000) + (64 #x43f0000000000000))) +(spec (fcvt_to_uint ty x) + (modifies clif_trap) + (provide + (= (:bits ty) (widthof result)) + (let ( + (s (widthof x)) + (d (widthof result))) + (and + (=> (not clif_trap) (= result (fp.to_ubv d (to_fp_from_fp d x)))) + + ;; Trap if input is NaN or does not fit in the integer type + ;; if xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0 then + (= clif_trap (or + (fp.isNaN x) + (fp.ge x (to_fp_from_fp s (conv_to d (neg_min_int_times_two_as_fp! d)))) + (fp.le x (to_fp_from_fp s (conv_to d (fp_minus_one! d)))))))))) +(instantiate fcvt_to_uint fcvt) + +(macro (min_int_as_fp w n) + (switch n + (32 (fp_i32_min! w)) + (64 (fp_i64_min! w)))) +(macro (neg_min_int_as_fp w n) + (switch n + (32 (fp_minus_i32_min! w)) + (64 (fp_minus_i64_min! w)))) +(spec (fcvt_to_sint ty x) + (modifies clif_trap) + (provide + (= (:bits ty) (widthof result)) + (let ( + (s (widthof x)) + (d (widthof result))) + (and + (=> (not clif_trap) (= result (fp.to_sbv d (to_fp_from_fp d x)))) + + ;; Trap if input is NaN or does not fit in the integer type + ;; + ;; Note f64 to i32 case takes a different form according to the reference interpreter. + ;; + ;; Reference interpreter i32_convert `trunc_f32_s`: + ;; if xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) then + ;; Reference interpreter i32_convert `trunc_f64_s`: + ;; if xf >= -.Int32.(to_float min_int) || xf <= Int32.(to_float min_int) -. 1.0 then + ;; Reference interpreter i64_convert `trunc_f32_s`: + ;; if xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) then + ;; Reference interpreter i64_convert `trunc_f64_s`: + ;; if xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) then + (= clif_trap (or + (fp.isNaN x) + (fp.ge x (neg_min_int_as_fp! s d)) + (if (and (= s 64) (= d 32)) + (fp.le x (fp.sub (min_int_as_fp! s d) (fp_one! s))) + (fp.lt x (min_int_as_fp! s d))))))))) +(instantiate fcvt_to_sint fcvt) + + +; Specification derived from WebAssembly Specification prose (Conversions, section 4.3.4). +(spec (fdemote ty z) + (match + ;; Demote only can return bv-32 as written + (= (widthof result) 32)) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a canonical NaN, then return an element of nans{} (i.e., a canonical NaN of size N). + ; Else if z is a NaN, then return an element of nans{±nan(1)} (i.e., any NaN of size N). + (if (fp.isNaN z) + ;; Note: derived from Wasm reference interpreter: + ;; https://github.com/WebAssembly/spec/blob/268a03da8576cc491d708777e69676724938aec9/interpreter/exec/f32_convert.ml#L4 + (bvor #x7fc00000 + (extract 31 0 + (bvor + (bvshl_int! (bvlshr_int! z 63) 31) + (bvlshr_int! (bvshl_int! z 12) 41)))) + ; Else if z is an infinity, then return that infinity. + (if (fp.isInfinite z) + (if (fp.isNegative z) (fp.-oo 32) (fp.+oo 32)) + ; Else if z is a zero, then return that zero. + (if (fp.isZero z) + (if (fp.isNegative z) (fp.-zero 32) (fp.+zero 32)) + ; Else,return float(z) + (to_fp_from_fp 32 z))))))) +(instantiate fdemote + ((args (named Type) (bv 64)) (ret (bv 32)))) + +; Specification derived from WebAssembly Specification prose (Conversions, section 4.3.4). +(spec (fpromote ty z) + (match + ;; Promote only can return bv-32 as written + (= (widthof result) 64)) + (provide + (= (:bits ty) (widthof result)) + (= result + ; If z is a canonical NaN, then return an element of nans{} (i.e., a canonical NaN of size N). + ; Else if z is a NaN, then return an element of nans{±nan(1)} (i.e., any arithmetic NaN of size N). + (if (fp.isNaN z) + ;; Note: derived from Wasm reference interpreter: + ;; https://github.com/WebAssembly/spec/blob/5d12bd74c49932deb7ab4bae3d29bf106f19d10b/interpreter/exec/f64_convert.ml#L4 + (bvor #x7ff8000000000000 + (bvor + (bvshl_int! (bvlshr_int! (zero_ext 64 z) 31) 63) + (bvlshr_int! (bvshl_int! (zero_ext 64 z) 41) 12))) + ; Else, return z. + (to_fp_from_fp 64 z))))) +(instantiate fpromote + ((args (named Type) (bv 32)) (ret (bv 64)))) + +;;;; CLIF Instruction Tags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(attr select_spectre_guard (tag spectre)) +(attr fence (tag TODO)) + +; Use Z3 solver for the following instructions. +(attr fadd (tag solver_z3)) +(attr fmul (tag solver_z3)) +(attr fdiv (tag solver_z3)) +(attr sqrt (tag solver_z3)) + +(attr cls (tag solver_z3)) +(attr clz (tag solver_z3)) +(attr ctz (tag solver_z3)) +(attr popcnt (tag solver_z3)) diff --git a/cranelift/codegen/src/spec/inst_tags.isle b/cranelift/codegen/src/spec/inst_tags.isle new file mode 100644 index 000000000000..d7b7d4a9fc64 --- /dev/null +++ b/cranelift/codegen/src/spec/inst_tags.isle @@ -0,0 +1,249 @@ +;;;; Verification Tags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(attr select + (tag wasm_category_stack) + (tag wasm_proposal_mvp)) + +(attr icmp + (tag wasm_category_comparison) + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr iconst + (tag wasm_category_const) + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr isub + (tag wasm_category_binary) + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr uadd_overflow_trap + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr uload8 + (tag wasm_category_loads) + (tag wasm_proposal_mvp)) + +(attr uload16 + (tag wasm_category_loads) + (tag wasm_proposal_mvp)) + +(attr sload8 + (tag wasm_category_loads) + (tag wasm_proposal_mvp)) + +(attr sload16 + (tag wasm_category_loads) + (tag wasm_proposal_mvp)) + +(attr sload32 + (tag wasm_category_loads) + (tag wasm_proposal_mvp)) + +(attr uload32 + (tag wasm_category_loads) + (tag wasm_proposal_mvp)) + +(attr load + (tag wasm_category_loads) + (tag wasm_proposal_mvp)) + +(attr store + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr istore8 + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr istore16 + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr istore32 + (tag wasm_category_stores) + (tag wasm_proposal_mvp)) + +(attr f32const + (tag wasm_category_const) + (tag wasm_proposal_mvp)) + +(attr f64const + (tag wasm_category_const) + (tag wasm_proposal_mvp)) + +(attr clz + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr ctz + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr popcnt + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr sextend + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr uextend + (tag wasm_category_comparison) + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr ireduce + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr sqrt + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr ceil + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr floor + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr trunc + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr nearest + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fabs + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fneg + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fcvt_from_uint + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fcvt_from_sint + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fpromote + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fdemote + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fcvt_to_sint + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr fcvt_to_uint + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr bitcast + (tag wasm_category_unary) + (tag wasm_proposal_mvp)) + +(attr iadd + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr band + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr bor + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr bxor + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr ishl + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr sshr + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr ushr + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr rotl + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr rotr + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fadd + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fsub + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr imul + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fmul + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fdiv + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr sdiv + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr udiv + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr srem + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr urem + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fmin + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fmax + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fcopysign + (tag wasm_category_binary) + (tag wasm_proposal_mvp)) + +(attr fcmp + (tag wasm_category_comparison) + (tag wasm_proposal_mvp)) diff --git a/cranelift/codegen/src/spec/prelude_spec.isle b/cranelift/codegen/src/spec/prelude_spec.isle new file mode 100644 index 000000000000..c9c5266e02ab --- /dev/null +++ b/cranelift/codegen/src/spec/prelude_spec.isle @@ -0,0 +1,82 @@ +;;;; Common spec macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Generic helpers + +; unspecified value. +(macro (unspecified) (with (x) x)) + +;; Boolean helpers + +; Exclusive or +(macro (xor a b) (and (or a b) (not (and a b)))) + +;; Bit-vector helpers + +; n-bit bitvector with value zero. +(macro (bvzero n) (zero_ext n #b0)) + +; n-bit bitvector with value one. +(macro (bvone n) (zero_ext n #b1)) + +; n-bit bitvector with value all ones. +(macro (bvones n) (bvnot (bvzero! n))) + +; bit-vector shift left by an integer amount. +(macro (bvshl_int x s) (bvshl x (int2bv (widthof x) s))) + +; bit-vector logical right shift by an integer amount. +(macro (bvlshr_int x s) (bvlshr x (int2bv (widthof x) s))) + +; n-bit bitvector with low m bits set. +(macro (low_bits_mask n m) (bvsub (bvshl (bvone! n) (int2bv n m)) (bvone! n))) + +; Shift mask for m bits (power of two), as an n-bit bitvector. +(macro (shift_mask n m) (bvsub (int2bv n m) (bvone! n))) + +; bitvector zero test +(macro (bv_is_zero x) (= x (bvzero! (widthof x)))) + +; n-bit bitvector with the top bit set. +(macro (bv_top_bit_set n) (bvnot (bvlshr_int! (bvones! n) 1))) + +; Convert a boolean to a single bit. +(macro (bool2bit b) (if b #b1 #b0)) + +; Convert number of bits to number of bytes. +(macro (bits2bytes bits) (bvlshr_int! bits 3)) + +;; Floating-point helpers + +; Whether two floating-point values have the same sign. +(macro (fp_equal_sign x y) (= (fp.isPositive x) (fp.isPositive y))) + +; Whether two floating-point values have the opposite signs, without +; using SMT function that are nondeterministic for NaN. +(macro (fp_equal_sign_inc_nan x y) + (= + (bvand (bv_top_bit_set! (widthof x)) x) + (bvand (bv_top_bit_set! (widthof y)) y))) + +; Whether two floating-point values have the opposite signs. +(macro (fp_opposite_sign x y) (not (fp_equal_sign! x y))) + +; Build a floating-point zero with the given sign and width. +(macro (fp_signed_zero negative w) (if negative (fp.-zero w) (fp.+zero w))) + +; Build a floating-point infinity with the given sign and width. +(macro (fp_signed_inf negative w) (if negative (fp.-oo w) (fp.+oo w))) + +; Magnitude of a floating-point value. (Clear the top sign bit.) +(macro (fp_magnitude x) (bvlshr_int! (bvshl_int! x 1) 1)) + +; Bit position of the top fraction bit in a floating-point value. +(macro (fp_topfrac_bit w) (switch w (32 22) (64 51))) + +; Bit-vector with the top fraction bit set. +(macro (fp_topfrac_bit_set w) (bvshl_int! (bvone! w) (fp_topfrac_bit! w))) + +; Bit position of the sign bit in a floating-point value. +(macro (fp_sign_bit w) (switch w (32 31) (64 63))) + +; Bit-vector with the sign bit set. +(macro (fp_sign_bit_set w) (bvshl_int! (bvone! w) (fp_sign_bit! w))) diff --git a/cranelift/codegen/src/spec/state.isle b/cranelift/codegen/src/spec/state.isle new file mode 100644 index 000000000000..da419708f899 --- /dev/null +++ b/cranelift/codegen/src/spec/state.isle @@ -0,0 +1,6 @@ +;; Execution state common shared by all backends. + +; Whether a trap has occurred. +(state exec_trap + (type Bool) + (default (not exec_trap))) diff --git a/cranelift/isle/docs/language-reference.md b/cranelift/isle/docs/language-reference.md index 3ec001d2b109..c1df555386c4 100644 --- a/cranelift/isle/docs/language-reference.md +++ b/cranelift/isle/docs/language-reference.md @@ -1554,20 +1554,38 @@ The grammar accepted by the parser is as follows: | "(" "model" ")" | "(" "form"
")" | "(" "instantiate" ")" + | "(" "attr" ")" + | "(" "macro" ")" + | "(" "state" ")" - ::= "(" * ")" [ ] - ::= "(" "provide" * ")" - ::= "(" "require" * ")" + ::= "(" * ")" * + ::= | | | + ::= "(" "provide" * ")" + ::= "(" "require" * ")" + ::= "(" "match" * ")" + ::= "(" "modifies" [ ] ")" ::= "(" "type" ")" - | "(" "enum" * ")" + | "(" "const" ")" ::= "Bool" | "Int" | "Unit" + | "!" ;; unspecified + | "_" ;; inferred | "(" "bv" [ ] ")" + | "(" "struct" * ")" + | "(" "named" ")" - ::= "(" [ ] ")" + ::= "(" ")" + + ::= [ "rule" ] * + ::= "(" "veri" ( "chain" | "priority" ) ")" + | "(" "tag" ")" + + ::= "(" * ")" + + ::= "(" "type" ")" "(" "default" ")" ::= * @@ -1579,35 +1597,640 @@ The grammar accepted by the parser is as follows: | "true" | "false" | | "(" "switch" * ")" + | "(" "let" "(" * ")" ")" + | "(" "with" "(" * ")" ")" + | "(" "match" * ")" + | "(" "struct" * ")" + | "(" "macro" "(" * ")" ")" + | "(" "as" ")" + | "(" "?" ")" ;; enum discriminator test, token "Variant?" + | "(" "!" * ")" ;; macro expansion, token "name!" + | "(" ":" ")" ;; field access, token ":field" + | "(" "." * ")" ;; enum constructor, token "Enum.Variant" | "(" * ")" | "(" ")" | "(" ")" - ::= "#b" [ "+" | "-" ] ("0".."1")+ - | "#x" [ "+" | "-" ] ("0".."9" | "A".."F" | "a".."f")+ + ::= "(" ")" + ::= "(" "(" * ")" ")" + ::= "(" ")" + + ::= "#b" ("0".."1")+ + | "#x" ("0".."9" | "A".."F" | "a".."f")+ ::= "(" ")" ::= "and" | "not" | "or" | "=>" | "=" | "<=" | "<" | ">=" | ">" + | "+" | "-" | "*" | "bvnot" | "bvand" | "bvor" | "bvxor" | "bvneg" | "bvadd" | "bvsub" | "bvmul" | "bvudiv" | "bvurem" | "bvsdiv" | "bvsrem" | "bvshl" | "bvlshr" | "bvashr" - | "bvsaddo" | "subs" + | "bvsaddo" | "bvule" | "bvult" | "bvugt" | "bvuge" | "bvsle" | "bvslt" | "bvsgt" | "bvsge" | "rotr" | "rotl" - | "extract" | "concat" | "conv_to" + | "extract" | "concat" | "replicate" | "conv_to" | "zero_ext" | "sign_ext" - | "int2bv" | "bv2int" + | "int2bv" | "bv2nat" | "widthof" | "if" | "switch" | "popcnt" | "rev" | "cls" | "clz" - | "load_effect" | "store_effect" + | "to_fp" | "to_fp_unsigned" | "to_fp_from_fp" + | "fp.to_ubv" | "fp.to_sbv" + | "fp.+oo" | "fp.-oo" | "fp.+zero" | "fp.-zero" | "fp.NaN" + | "fp.eq" | "fp.ne" | "fp.lt" | "fp.gt" | "fp.le" | "fp.ge" + | "fp.add" | "fp.sub" | "fp.mul" | "fp.div" + | "fp.min" | "fp.max" | "fp.neg" + | "fp.ceil" | "fp.floor" | "fp.sqrt" | "fp.trunc" | "fp.nearest" + | "fp.isZero" | "fp.isInfinite" | "fp.isNaN" + | "fp.isNegative" | "fp.isPositive" + + ::= "(" ")" + ::= "(" "args" * ")" + ::= "(" "ret" ")" +``` + + +## ISLE Verification Extensions — Structured Reference + +This section documents the verification-specific extensions to ISLE. + +These extensions allow ISLE definitions to be translated into logical formulas and verified using an SMT solver. + +At the top level of an ISLE file, the following definition forms are supported: + +1. `(model ...)` — specifies which SMT construct is used to model an ISLE type +2. `(form ...)` — defines a reusable, named collection of instantiations (e.g. monomorphizations to specific bit-widths) +3. `(instantiate ...)` — specifies which concrete type instantiations are verified for a term +4. `(spec ...)` — provides a specification of a term using logical expressions, including `provide` and `require` blocks +5. `(state ...)` — declares a program-state variable (e.g. modeling memory or traps) with a type and default value +6. `(attr ...)` — annotates a term or rule with a verification attribute (`(veri chain)`, `(veri priority)`, or `(tag ...)`) +7. `(macro ...)` — defines a reusable spec-expression macro, primarily for complex numeric logic such as floating-point reasoning + +Additionally, the verification language introduces a specification expression language `(spec-expr)` used within specifications. + + +### 1. Model: `(model ...)` + +#### 1.1 Formal Grammar + +```bnf + ::= "(" "type" ")" + | "(" "const" ")" + + ::= "Bool" + | "Int" + | "Unit" + | "!" ;; unspecified + | "_" ;; inferred + | "(" "bv" [ ] ")" + | "(" "struct" * ")" + | "(" "named" ")" + + ::= "(" ")" +``` + +#### 1.2 Semantics + +A `model` definition assigns an **SMT interpretation** to an ISLE type. + +This defines +```code +ISLE Type → SMT Sort +``` + +Without a `model` a type has no formal meaning in verification. + +The `model` therefore acts as the bridge between +- the ISLE type system +- the SMT solver's logical sorts + +A `model` body is either a `(type )` form, which maps the type to an +SMT sort, or a `(const )` form, which gives an external constant a +fixed symbolic value. The `(type ...)` form supports the following modelling +strategies: + +1. Primitive Type Model + +```lisp +(type T (primitive ...)) +(model T (type )) +``` + +This maps an ISLE type directly to an SMT sort (e.g. a bitvector). + +2. Struct (composite) model + +```lisp +(model T (type (struct + (field₁ ) + (field₂ ) + ...))) +``` + +This encodes an ISLE type as a composite SMT structure with fixed fields. Unlike +enums, structs do not have variants—every instance has all the specified +fields. Note that the `struct` is nested inside `(type ...)`. + +3. Enumeration Model + +Enumerations are not written with an explicit `model`. Instead, the verifier +derives an SMT datatype directly from an ISLE `enum` type declaration: + +```lisp +(type T (enum (Variant₁ ...) (Variant₂ ...) ...)) +``` + +Each variant becomes a constructor of a finite SMT datatype. + +#### 1.3 Examples + +**Example 1**: fixed bitvector width + +```lisp +(type UImm5 (primitive UImm5)) +(model UImm5 (type (bv 5))) +``` + +**Explanation**: This example models the ISLE type `UImm5` as a 5-bit SMT bitvector, in this case to represent small immediates in instruction encodings. +The `model` declaration ensures that the SMT solver interprets values of this type as bitvectors of width 5. + +**Example 2**: composite (struct) type + +```lisp +(type Imm12 (primitive Imm12)) +(model Imm12 + (type + (struct + (bits (bv 12)) + (shift12 Bool)))) +``` + +**Explanation**: This example models `Imm12` as a composite SMT structure. + +The model contains two fields: + +- `bits` — a 12-bit bitvector representing the immediate value +- `shift12` — a Boolean flag indicating whether the value is shifted - ::= "(" ")" +Composite models allow ISLE types to map to structured datatypes rather than primitive values. + +**Example 3**: Enumeration Type + +```lisp +(type BitOp + (enum + (RBit) + (Clz) + (Cls) + (Rev16) + (Rev32) + (Rev64) +)) +``` + +**Explanation**: Enumeration types are modeled as SMT datatypes with multiple variants. + +Each variant corresponds to a possible value of the ISLE type. +This allows the SMT solver to reason about which variant of the enum is active during verification. + +For many ISLE enums, the verifier derives types for that enum without an explicit `model`. + +**Example 4**: Parametric enum (variants that carry fields) +```lisp +(type CondBrKind extern + (enum + (Zero (r Reg)) + (NotZero (r Reg)) + (Cond (cc Cond)))) +``` + +**Explanation**: Variants of an enum may also carry additional data. + +In this example, the `CondBrKind` type represents different conditional branch kinds. +Some variants carry additional information, such as a register or condition code. +These fields are modeled as part of the SMT datatype. + +### 2. Instantiation: `(instantiate ...)` + +#### 2.1 Formal Grammar + +```bnf + ::= * + | + + ::= "(" ")" ::= "(" "args" * ")" - ::= "(" "ret" * ")" - ::= "(" "canon" * ")" + ::= "(" "ret" ")" +``` + +#### 2.2 Semantics + +`instantiate` declares concrete verification instances of a term. + +Verification signature may contain abstract types (for example, bitvectors with unspecified width). Instantiation specializes these generic signatures into concrete types that the SMT solver can reason about. + +This process is similar to monomorphization in compilers: +```lisp +generic specification + ↓ +instantiate + ↓ +concrete verification instance +``` + +For example, +```lisp +(bv) +``` +may be instantiated as + +```lisp +(bv 8) +(bv 16) +(bv 32) +(bv 64) +``` + +Each instantiation produces a separate SMT verification obligation. + +Two forms of instantiation exists: + +1. Direct Signature Instantiation: concrete signatures written explicitly +2. Form-based Instantiation: a previously defined `form` is used + +#### 2.3 Signature Definitions + +##### 2.3.1 Formal Grammar +```bnf + ::= "(" ")" + ::= "(" "args" * ")" + ::= "(" "ret" ")" +``` + +##### 2.3.2 Semantic Meaning + +A signature defines +- argument SMT sorts (`args`) +- the return SMT sort (`ret`) + +It represents a function type: +```code +(args₁ × args₂ × …) → ret +``` + +#### 2.4 Form: `(form ...)` + +##### 2.4.1 Formal Grammar + +```bnf + ::= * +``` + +#### 2.4.2 Semantics + +A `form` defines a reusable collection of verification signatures for a term. + +It does not define behavior; rather, it constrains which combinations of types are considered valid during verification. Forms provide a way to declare a family of related type instantiations once and reuse them across multiple terms. + +For example, many binary operations on integers can be expressed as taking two bit-vector arguments and producing a bit-vector result of the same size, where the size ranges over powers of two (e.g., 8, 16, 32, 64, up to 128 bits). A form captures this pattern abstractly, allowing all such instances to be represented uniformly without redefining each case individually. + +#### 2.5.3 Example (form) +```lisp +(form + bv_binary_8_to_64 + ((args (named Type) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64)))) +``` + +**Explanation:** +This declares that `bv_binary_8_to_64` supports four types of monomorphizations: + - 8 x 8 -> 8 + - 16 x 16 -> 16 + - 32 x 32 -> 32 + - 64 x 64 -> 64 + + +#### 2.6 Example (instantiation) + +```lisp +(spec (iadd ty x y) + (provide (= result (bvadd x y)))) + +(instantiate iadd + ((args (named Type) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) + ((args (named Type) (bv 128) (bv 128)) (ret (bv 128)))) +``` + +**Explanation:** +- The `spec` defines integer addition abstractly using bitvector addition +- `instantiate` block then provides concrete verification instances for 8, 16, 32, 64, 128 bit +- Each instantiation generates a separate SMT verification obligation for that bit-width + +### 3. Specification: `(spec ...)` + +A `spec` defines a specification over an ISLE term. + +#### 3.1. Formal Grammar +```bnf + ::= "(" "spec" "(" * ")" * ")" + + ::= | | | + ::= "(" "provide" * ")" + ::= "(" "require" * ")" + ::= "(" "match" * ")" + ::= "(" "modifies" [ ] ")" +``` + +The clauses may appear in any order, and each may be repeated. + +#### 3.2 Semantics +A `spec` definition declares a **specification** for an ISLE term. + +A specification + +```lisp +(spec (f x₁ … xₙ) + (require R₁ … Rₖ) + (provide P₁ … Pₘ)) +``` + +states a specification for the term `f`. Let `r = f(x₁ … xₙ)` be the result of applying `f` to its arguments. The specification asserts that whenever all required conditions hold, + +```lisp +R₁ ∧ … ∧ Rₖ +``` + +the provided guarantees must hold for the result: +```lisp +P₁ ∧ … ∧ Pₘ +``` + +In other words, +```lisp +(R₁ ∧ … ∧ Rₖ) ⇒ (P₁ ∧ … ∧ Pₘ). +``` + +**Semantic Roles:** + +- The `spec` identifies the term and its formal parameters. +- A `(provide ...)` clause encodes postconditions. +- A `(require ...)` clause encodes preconditions. +- If `require` is omitted, it defaults to `true`. +- `(match ...)` supports pattern-related constraints +- `(modifies ...)` describes state mutation effects + +#### 3.3 Example + +```lisp +(decl value_regs (Reg Reg) ValueRegs) + +(spec (value_regs arg1 arg2) + (provide + (= (:lo result) arg1) + (= (:hi result) arg2))) +``` + +**Explanation:** +- `value_regs` is declared as a term +- The specification states that the resulting value has: + - low half equal to `arg1` + - high half equal to `arg2` +- There is no `require` block, so the specification is unconditional. + +#### 3.4 Specific Expression Language (`spec-expr`) + +`spec-expr` is not a top-level feature, but is the expression language used within `require`, `provide`, and `match`. + +##### 3.4.1 Formal Grammar + +```bnf + + ::= + | + | "true" | "false" + | + | "(" "switch" * ")" + | "(" "let" "(" * ")" ")" + | "(" "with" "(" * ")" ")" + | "(" "match" * ")" + | "(" "struct" * ")" + | "(" "macro" "(" * ")" ")" + | "(" "as" ")" + | "(" "?" ")" ;; enum discriminator test, token "Variant?" + | "(" "!" * ")" ;; macro expansion, token "name!" + | "(" ":" ")" ;; field access, token ":field" + | "(" "." * ")" ;; enum constructor, token "Enum.Variant" + | "(" * ")" + | "(" ")" + | "(" ")" + + ::= "(" ")" + ::= "(" "(" * ")" ")" + ::= "(" ")" + + ::= "#b" ("0".."1")+ + | "#x" ("0".."9" | "A".."F" | "a".."f")+ + + ::= "(" ")" + + ::= "and" | "not" | "or" | "=>" + | "=" | "<=" | "<" | ">=" | ">" + | "+" | "-" | "*" + | "bvnot" | "bvand" | "bvor" | "bvxor" + | "bvneg" | "bvadd" | "bvsub" | "bvmul" + | "bvudiv" | "bvurem" | "bvsdiv" | "bvsrem" + | "bvshl" | "bvlshr" | "bvashr" + | "bvsaddo" + | "bvule" | "bvult" | "bvugt" | "bvuge" + | "bvsle" | "bvslt" | "bvsgt" | "bvsge" + | "rotr" | "rotl" + | "extract" | "concat" | "replicate" | "conv_to" + | "zero_ext" | "sign_ext" + | "int2bv" | "bv2nat" + | "widthof" + | "if" | "switch" + | "popcnt" | "rev" | "cls" | "clz" + | "to_fp" | "to_fp_unsigned" | "to_fp_from_fp" + | "fp.to_ubv" | "fp.to_sbv" + | "fp.+oo" | "fp.-oo" | "fp.+zero" | "fp.-zero" | "fp.NaN" + | "fp.eq" | "fp.ne" | "fp.lt" | "fp.gt" | "fp.le" | "fp.ge" + | "fp.add" | "fp.sub" | "fp.mul" | "fp.div" + | "fp.min" | "fp.max" | "fp.neg" + | "fp.ceil" | "fp.floor" | "fp.sqrt" | "fp.trunc" | "fp.nearest" + | "fp.isZero" | "fp.isInfinite" | "fp.isNaN" + | "fp.isNegative" | "fp.isPositive" + +``` + +##### 3.4.2 Operators (`spec-op`) +Operators include: +- Boolean logic: `and`, `or`, `not`, `=>` +- Integer arithmetic (`+`, `-`, `*`) +- Equality & comparisons +- Bitvector arithmetic (`bvadd`, `bvmul`, etc.) +- Bitwise ops (`bvand`, `bvor`, etc.) +- Extraction/concatenation/replication +- Conversions (`int2bv`, `bv2nat`, `conv_to`, `zero_ext`, `sign_ext`) +- Control (`if`, `switch`) +- Floating point (`fp.add`, `fp.sqrt`, `to_fp`, `fp.isNaN`, etc.) + +In most cases, these map directly to SMT operators. + +##### 3.4.3 Semantics +`spec-expr` defines a first-order term language over: +- integers +- bitvectors +- booleans +- state effects + +Every expression is lowered to an SMT term. + +For example: +```lisp +(= result (bvadd x y)) ``` +becomes an SMT equality constraint. + +### 4. State, Attributes, and Macros + +#### 4.1 State: `(state ...)` + +```bnf + ::= "(" "type" ")" "(" "default" ")" +``` + +A `state` declaration introduces a named program-state variable (for example, a +memory model) that specs may read and write. It carries a type (a ``) +and a default value (a ``). A spec mutates state via its `(modifies + [])` clause. + +```lisp +; Parameters of a CLIF load operation. +(state clif_load + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)))) + (default + (not (:active clif_load)))) +``` + +#### 4.2 Attributes: `(attr ...)` + +```bnf + ::= [ "rule" ] * + ::= "(" "veri" ( "chain" | "priority" ) ")" + | "(" "tag" ")" +``` + +An `attr` annotates a term (or, with the `rule` keyword, a named rule) with +verification metadata. The `(veri chain)` and `(veri priority)` attributes +control how the verifier treats the target, and `(tag )` attaches a +free-form tag. + +```lisp +(attr rule my_rule (veri chain)) +``` + +#### 4.3 Macros: `(macro ...)` + +```bnf + ::= "(" * ")" +``` + +A `macro` defines a named, parameterized `spec-expr` template that can be +expanded inside other spec expressions using the `name!` invocation form (see +the `` grammar). We use macros primarily to factor out complex +numeric logic such as floating-point reasoning. + +### Summary + +The ISLE verification subset includes: +- Logical specification (`spec`) +- SMT type interpretation (`model`) +- Reusable verification signatures (`form`) +- Concrete instantiation (`instantiate`) +- Program state (`state`), verification attributes (`attr`), and spec macros (`macro`) + +Together these form a layered architecture: +1. **Type Modelling Layer** - via `model` +2. **Signature Layer** - via `form` +3. **Instantiation Layer** - via `instantiate` +4. **Specification Layer** - via `spec` + +This design cleanly separates typing, instantiation, and logical reasoning within ISLE's verification framework. + +## Running the ISLE Verifier + +The ISLE verifier can be run on individual rules or rule chains using the `veri` tool included in the Cranelift repository. + +### Location of the Verifier + +Navigate to the verifier directory + +```bash +cd cranelift/isle/veri/veri +``` + +The main entry point for verification is the helper script: + +```bash +script/veri.sh +``` + +### Verifying an individual rule + +The verifier can check a specific ISLE rule using `--filter` option. + +```bash +./script/veri.sh -a x64 -- --filter include:rule: --solver z3 +``` + +The verifier will translate the rule and its specification into SMT constraints and check them using the selected SMT solver. + +### Verifying Rule Chains + +Rules in ISLE may form chains, where the result of one rule becomes the input to another. + +The verifier can analyze these chains automatically. When a rule is selected using the filter mechanism, the verifier will also include any dependent rules required to construct the full rule chain. + +This allows verification to ensure that the entire rewrite sequence preserves the specification. + +### Expected Output + +A successful verification run produces output similar to: + +```c +type solution status = solved +// The solver successfully resolved the type constraints +applicability = applicable +// The rule's preconditions are satisfiable +verification = success +// The SMT solver proved the specification holds +``` + +### Debugging Verification + +Additional debugging information can be enabled using environment variables: + +```bash +RUST_BACKTRACE=1 RUST_LOG=DEBUG \ +./script/veri.sh -a x64 -- \ +--filter include:rule:load_narrow \ +--solver z3 \ +--debug +``` + +This enables: +- Rust stack traces +- detailed logging from the verifier +- debugging output for SMT generation diff --git a/cranelift/isle/isle/build.rs b/cranelift/isle/isle/build.rs index ceb30591af97..697a10514d60 100644 --- a/cranelift/isle/isle/build.rs +++ b/cranelift/isle/isle/build.rs @@ -1,4 +1,4 @@ -use std::fmt::Write; +use std::{fmt::Write, path::PathBuf}; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -8,6 +8,11 @@ fn main() { std::env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"), ); + isle_tests(&out_dir); + isle_printer_tests(&out_dir); +} + +fn isle_tests(out_dir: &std::path::PathBuf) { let mut out = String::new(); emit_tests(&mut out, "isle_examples/pass", "run_pass"); @@ -23,7 +28,24 @@ fn main() { std::fs::write(output, out).unwrap(); } +fn isle_printer_tests(out_dir: &std::path::PathBuf) { + let mut out = String::new(); + + emit_tests(&mut out, "isle_examples/pass", "run_print"); + emit_tests(&mut out, "../veri/veri/filetests/pass", "run_print"); + emit_tests(&mut out, "../../codegen/src", "run_print"); + emit_tests(&mut out, "../../codegen/src/opts", "run_print"); + emit_tests(&mut out, "../../codegen/src/isa/x64", "run_print"); + emit_tests(&mut out, "../../codegen/src/isa/aarch64", "run_print"); + emit_tests(&mut out, "../../codegen/src/isa/riscv64", "run_print"); + + let output = out_dir.join("isle_printer_tests.rs"); + std::fs::write(output, out).unwrap(); +} + fn emit_tests(out: &mut String, dir_name: &str, runner_func: &str) { + let dir_path = PathBuf::from(dir_name); + let test_name = dir_path.file_name().unwrap().to_string_lossy(); for test_file in std::fs::read_dir(dir_name).unwrap() { let test_file = test_file.unwrap().file_name().into_string().unwrap(); if !test_file.ends_with(".isle") { @@ -32,7 +54,11 @@ fn emit_tests(out: &mut String, dir_name: &str, runner_func: &str) { let test_file_base = test_file.replace(".isle", ""); writeln!(out, "#[test]").unwrap(); - writeln!(out, "fn test_{runner_func}_{test_file_base}() {{").unwrap(); + writeln!( + out, + "fn test_{runner_func}_{test_name}_{test_file_base}() {{" + ) + .unwrap(); writeln!(out, " {runner_func}(\"{dir_name}/{test_file}\");").unwrap(); writeln!(out, "}}").unwrap(); } diff --git a/cranelift/isle/isle/isle_examples/pass/veri_spec.isle b/cranelift/isle/isle/isle_examples/pass/veri_spec.isle index daa0d8ead7a4..fcc1cf4fa5d3 100644 --- a/cranelift/isle/isle/isle_examples/pass/veri_spec.isle +++ b/cranelift/isle/isle/isle_examples/pass/veri_spec.isle @@ -1,19 +1,19 @@ (form bv_unary_8_to_64 - ((args (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 64)) (canon (bv 64))) + ((args (bv 8)) (ret (bv 8))) + ((args (bv 16)) (ret (bv 16))) + ((args (bv 32)) (ret (bv 32))) + ((args (bv 64)) (ret (bv 64))) ) (spec (A i j) (provide (= (if true (= i j) (= i (bvneg j))) (=> false true)))) -(instantiate A ((args (bv 8)) (ret (bv 8)) (canon (bv 8)))) +(instantiate A ((args (bv 8)) (ret (bv 8)))) (decl A (u8 u8) u8) (spec (B i) (provide (= (bvadd i #xff) #b00000000)) (require (= (= 1 2) false))) -(instantiate B unary_bv_8_to_64) +(instantiate B bv_unary_8_to_64) (decl B (u8) u8) (rule first 1 (A x x) x) diff --git a/cranelift/isle/isle/src/ast.rs b/cranelift/isle/isle/src/ast.rs index 964c3187f894..bd196c3f2bda 100644 --- a/cranelift/isle/isle/src/ast.rs +++ b/cranelift/isle/isle/src/ast.rs @@ -13,8 +13,11 @@ pub enum Def { Rule(Rule), Extractor(Extractor), Decl(Decl), + Attr(Attr), Spec(Spec), + SpecMacro(SpecMacro), Model(Model), + State(State), Form(Form), Instantiation(Instantiation), Extern(Extern), @@ -57,6 +60,15 @@ pub struct Variant { pub pos: Pos, } +impl Variant { + pub fn full_name(enum_name: &Ident, variant_name: &Ident) -> Ident { + Ident( + format!("{}.{}", enum_name.0, variant_name.0), + variant_name.1, + ) + } +} + /// The fields of a struct or enum variant, formatted as a struct or tuple. #[derive(Clone, PartialEq, Eq, Debug)] pub enum Fields { @@ -114,6 +126,58 @@ pub struct Decl { pub pos: Pos, } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Instantiation { + pub term: Ident, + pub form: Option, + pub signatures: Vec, + pub pos: Pos, +} + +/// An attribute applied to a declaration. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Attr { + pub target: AttrTarget, + pub kinds: Vec, + pub pos: Pos, +} + +/// Object an attribute applies to. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum AttrTarget { + Term(Ident), + Rule(Ident), +} + +/// A kind of attribute that can be applied to a term declaration or rule. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum AttrKind { + /// In verification, apply rule chaining to this term. + /// + /// A term marked for chaining may omit a specification. Instead, all + /// possible applications of rules to this term will be generated and + /// verified. + Chain, + + /// In verification, declare that the correctness of lower priority rules + /// depends on this rule not matching. + /// + /// During rule expansion, any higher-priority overlapping rules that have + /// the priority tag will have their match conditions negated and added to + /// the verification conditions. + /// + /// Note that care must be taken when using this tag: if the specification + /// for the match conditions of the higher priority rule are an + /// over-approximation of reality, then the assumptions made by lower + /// priority rules will be an under-approximation. In an extreme case this + /// may cause the verifier to determine the lower priority rule never + /// applies. In a more subtle case, it could cause bugs to be missed. + Priority, + + /// Tag allows for categorizing terms and rules. + Tag(Ident), +} + /// An expression used to specify term semantics, similar to SMT-LIB syntax. #[derive(Clone, PartialEq, Eq, Debug)] pub enum SpecExpr { @@ -124,8 +188,8 @@ pub enum SpecExpr { }, /// An operator that matches a constant bitvector value. ConstBitVec { - val: i128, - width: i8, + val: u128, + width: usize, pos: Pos, }, /// An operator that matches a constant boolean value. @@ -133,32 +197,111 @@ pub enum SpecExpr { val: bool, pos: Pos, }, - /// The Unit constant value. - ConstUnit { - pos: Pos, - }, // A variable Var { var: Ident, pos: Pos, }, + // As expression specifies the intended type of the expression. Functionally + // it is the identity. Analogous to qualified identifiers in SMT-LIB. + As { + x: Box, + ty: ModelType, + pos: Pos, + }, + /// Struct field access. + Field { + field: Ident, + x: Box, + pos: Pos, + }, + /// Discriminator is a predicate that tests the variant of an enum value. + Discriminator { + variant: Ident, + x: Box, + pos: Pos, + }, /// An application of a type variant or term. Op { op: SpecOp, args: Vec, pos: Pos, }, + /// Enum pattern matching. + Match { + x: Box, + arms: Vec, + pos: Pos, + }, + /// Let bindings. + Let { + defs: Vec<(Ident, SpecExpr)>, + body: Box, + pos: Pos, + }, + /// Introduce new uninitialized variables. + With { + decls: Vec, + body: Box, + pos: Pos, + }, + /// Inline macro definition, or lambda. + Macro { + /// Parameter names. + params: Vec, + /// Macro expansion. + body: Box, + pos: Pos, + }, + /// Macro expansion. + Expand { + name: Ident, + args: Vec, + pos: Pos, + }, /// Pairs, currently used for switch statements. Pair { l: Box, r: Box, + pos: Pos, }, - /// Enums variant values (enums defined by model) + /// Construct enum variant. Enum { name: Ident, + variant: Ident, + args: Vec, + pos: Pos, + }, + /// Construct struct value. + Struct { + fields: Vec, + pos: Pos, }, } +impl SpecExpr { + pub fn pos(&self) -> Pos { + match self { + &Self::ConstInt { pos, .. } + | &Self::ConstBitVec { pos, .. } + | &Self::ConstBool { pos, .. } + | &Self::Var { pos, .. } + | &Self::As { pos, .. } + | &Self::Field { pos, .. } + | &Self::Discriminator { pos, .. } + | &Self::Op { pos, .. } + | &Self::Match { pos, .. } + | &Self::Let { pos, .. } + | &Self::With { pos, .. } + | &Self::Macro { pos, .. } + | &Self::Expand { pos, .. } + | &Self::Pair { pos, .. } + | &Self::Enum { pos, .. } + | &Self::Struct { pos, .. } => pos, + } + } +} + /// An operation used to specify term semantics, similar to SMT-LIB syntax. #[derive(Clone, PartialEq, Eq, Debug)] pub enum SpecOp { @@ -169,6 +312,11 @@ pub enum SpecOp { Not, Imp, + // Integer arithmetic operations + Add, + Sub, + Mul, + // Integer comparisons Lt, Lte, @@ -214,9 +362,39 @@ pub enum SpecOp { ZeroExt, SignExt, Concat, + Replicate, + + // Floating point (IEEE 754-2008) + FPEq, + FPNe, + FPLt, + FPGt, + FPLe, + FPGe, + FPPositiveInfinity, + FPNegativeInfinity, + FPPositiveZero, + FPNegativeZero, + FPNaN, + FPAdd, + FPSub, + FPMul, + FPDiv, + FPMin, + FPMax, + FPNeg, + FPCeil, + FPFloor, + FPSqrt, + FPTrunc, + FPNearest, + FPIsZero, + FPIsInfinite, + FPIsNaN, + FPIsNegative, + FPIsPositive, // Custom encodings - Subs, Popcnt, Clz, Cls, @@ -225,15 +403,52 @@ pub enum SpecOp { // Conversion operations ConvTo, Int2BV, - BV2Int, + BV2Nat, + ToFP, + ToFPUnsigned, + ToFPFromFP, + FPToUBV, + FPToSBV, WidthOf, // Control operations If, Switch, +} + +/// Arm of a spec match expression. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Arm { + pub variant: Ident, + pub args: Vec, + pub body: SpecExpr, + pub pos: Pos, +} + +/// Field initializer in a struct constructor. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FieldInit { + pub name: Ident, + pub value: Box, + pub pos: Pos, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct SpecMacro { + /// Macro name. + pub name: Ident, + /// Parameter names. + pub params: Vec, + /// Macro expansion. + pub body: SpecExpr, + pub pos: Pos, +} - LoadEffect, - StoreEffect, +/// State modification clause. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Modifies { + pub state: Ident, + pub cond: Option, } /// A specification of the semantics of a term. @@ -247,19 +462,43 @@ pub struct Spec { pub provides: Vec, /// Require statements, which express preconditions on the term pub requires: Vec, + /// Match conditions, which specify when a partial term returns a value. + pub matches: Vec, + /// State variables modified by the term. + pub modifies: Vec, + pub pos: Pos, } /// A model of an SMT-LIB type. #[derive(Clone, PartialEq, Eq, Debug)] pub enum ModelType { + /// Unspecified type. + /// + /// Unlike an auto-derived type, unspecified is a concrete type. However, + /// values of this type cannot be used for anything non-trivial. It is + /// intended to be used as a placeholder for a type that is not yet known, + /// but only appears in rules that are not yet covered by verification. + Unspecified, + /// Automatically deduced primitive type, left to type-inference to determine. + Auto, /// SMT-LIB Int Int, /// SMT-LIB Bool Bool, + /// Unit type. + Unit, /// SMT-LIB bitvector, but with a potentially-polymorphic width BitVec(Option), - /// Unit (removed before conversion to SMT-LIB) - Unit, + /// Structured type. + Struct(Vec), + /// Same model as the named type. + Named(Ident), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ModelField { + pub name: Ident, + pub ty: ModelType, } /// A construct's value in SMT-LIB @@ -267,8 +506,8 @@ pub enum ModelType { pub enum ModelValue { /// Correspond to ISLE types TypeValue(ModelType), - /// Correspond to ISLE enums, identifier is the enum variant name - EnumValues(Vec<(Ident, SpecExpr)>), + /// Corresponds to ISLE external constants. + ConstValue(SpecExpr), } /// A model of a construct into SMT-LIB (currently, types or enums) @@ -280,11 +519,22 @@ pub struct Model { pub val: ModelValue, } +/// Declare an element of global state accessible by verification specs. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct State { + /// Name of the state element. + pub name: Ident, + /// Type of the state element. + pub ty: ModelType, + /// Default specification, applied if the state is not modified. + pub default: SpecExpr, + pub pos: Pos, +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct Signature { pub args: Vec, pub ret: ModelType, - pub canonical: ModelType, pub pos: Pos, } @@ -295,14 +545,6 @@ pub struct Form { pub pos: Pos, } -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct Instantiation { - pub term: Ident, - pub form: Option, - pub signatures: Vec, - pub pos: Pos, -} - #[derive(Clone, PartialEq, Eq, Debug)] pub struct Rule { pub pattern: Pattern, diff --git a/cranelift/isle/isle/src/codegen.rs b/cranelift/isle/isle/src/codegen.rs index 6c34a55bdf41..182f517c8c20 100644 --- a/cranelift/isle/isle/src/codegen.rs +++ b/cranelift/isle/isle/src/codegen.rs @@ -602,7 +602,7 @@ impl Length for ContextIterWrapper {{ }; let scope = ctx.enter_scope(); - self.emit_block(&mut ctx, &root, sig.ret_kind, &last_expr, scope)?; + self.emit_block(&mut ctx, &root, sig.ret_kind, &last_expr, scope, options)?; } Ok(()) } @@ -663,6 +663,7 @@ impl Length for ContextIterWrapper {{ ret_kind: ReturnKind, last_expr: &str, scope: StableSet, + _options: &CodegenOptions, ) -> std::fmt::Result { ctx.begin_block()?; self.emit_block_contents(ctx, block, ret_kind, last_expr, scope) diff --git a/cranelift/isle/isle/src/compile.rs b/cranelift/isle/isle/src/compile.rs index a27c961cd224..4049b290cb62 100644 --- a/cranelift/isle/isle/src/compile.rs +++ b/cranelift/isle/isle/src/compile.rs @@ -3,7 +3,6 @@ use std::path::Path; use std::sync::Arc; -use crate::ast::Def; use crate::error::Errors; use crate::files::Files; use crate::{ast, codegen, overlap, recursion, sema}; @@ -65,43 +64,3 @@ pub fn from_files>( compile(files, &defs, options) } - -/// Construct the ISLE type and term environments for further analysis -/// (i.e., verification), without going all the way through codegen. -pub fn create_envs( - inputs: Vec, -) -> Result<(sema::TypeEnv, sema::TermEnv, Vec), Errors> { - let files = match Files::from_paths(inputs, &[]) { - Ok(files) => files, - Err((path, err)) => { - return Err(Errors::from_io( - err, - format!("cannot read file {}", path.display()), - )); - } - }; - let files = Arc::new(files); - let mut defs = Vec::new(); - for (file, src) in files.file_texts.iter().enumerate() { - let lexer = match crate::lexer::Lexer::new(file, src) { - Ok(lexer) => lexer, - Err(err) => return Err(Errors::new(vec![err], files)), - }; - - match crate::parser::parse(lexer) { - Ok(mut ds) => defs.append(&mut ds), - Err(err) => return Err(Errors::new(vec![err], files)), - } - } - let mut type_env = match sema::TypeEnv::from_ast(&defs) { - Ok(type_env) => type_env, - Err(errs) => return Err(Errors::new(errs, files)), - }; - // We want to allow annotations on terms with internal extractors, - // so we avoid expanding them within the sema rules. - let term_env = match sema::TermEnv::from_ast(&mut type_env, &defs, false) { - Ok(term_env) => term_env, - Err(errs) => return Err(Errors::new(errs, files)), - }; - Ok((type_env, term_env, defs)) -} diff --git a/cranelift/isle/isle/src/error.rs b/cranelift/isle/isle/src/error.rs index 87aec5a0228e..602377504f6f 100644 --- a/cranelift/isle/isle/src/error.rs +++ b/cranelift/isle/isle/src/error.rs @@ -5,13 +5,16 @@ use std::sync::Arc; use crate::{files::Files, lexer::Pos}; /// A collection of errors from attempting to compile some ISLE source files. +#[derive(Debug)] pub struct Errors { /// The individual errors. pub errors: Vec, pub(crate) files: Arc, } -impl std::fmt::Debug for Errors { +impl std::error::Error for Errors {} + +impl std::fmt::Display for Errors { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if self.errors.is_empty() { return Ok(()); @@ -227,6 +230,41 @@ impl Errors { } } +/// Builder for the `isle::Errors`. +pub struct ErrorsBuilder(Errors); + +impl ErrorsBuilder { + /// Start building an [Errors] object. + pub fn new() -> Self { + Self(Errors { + errors: Vec::new(), + files: Arc::new(Files::default()), + }) + } + + /// Return the built [Errors] object. + pub fn build(self) -> Errors { + self.0 + } + + /// Set the `errors` field of the under-construction [Errors] object. + pub fn errors(mut self, errors: Vec) -> Self { + self.0.errors = errors; + self + } + + /// Set the `errors` field of the under-construction [Errors] object to a single error. + pub fn error(self, error: Error) -> Self { + self.errors(vec![error]) + } + + /// Set the [Errors::files] field of the under-construction [Errors] object. + pub fn files(mut self, files: Arc) -> Self { + self.0.files = files; + self + } +} + impl Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { diff --git a/cranelift/isle/isle/src/lexer.rs b/cranelift/isle/isle/src/lexer.rs index 558cddc63a04..e8096d506441 100644 --- a/cranelift/isle/isle/src/lexer.rs +++ b/cranelift/isle/isle/src/lexer.rs @@ -35,6 +35,11 @@ impl Pos { Self { file, offset } } + /// Report whether the position is unknown. + pub fn is_unknown(&self) -> bool { + *self == Self::default() + } + /// Print this source position as `file.isle line 12`. pub fn pretty_print_line(&self, files: &Files) -> String { format!( diff --git a/cranelift/isle/isle/src/lib.rs b/cranelift/isle/isle/src/lib.rs index 369b1b815b85..c9e1cd603c61 100644 --- a/cranelift/isle/isle/src/lib.rs +++ b/cranelift/isle/isle/src/lib.rs @@ -7,7 +7,7 @@ macro_rules! declare_id { $name:ident ) => { $(#[$attr])* - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct $name(pub usize); impl $name { /// Get the index of this id. diff --git a/cranelift/isle/isle/src/parser.rs b/cranelift/isle/isle/src/parser.rs index 538d6dde2747..81fa750c1512 100644 --- a/cranelift/isle/isle/src/parser.rs +++ b/cranelift/isle/isle/src/parser.rs @@ -1,6 +1,7 @@ //! Parser for ISLE language. use crate::ast::*; + use crate::error::{Error, Span}; use crate::lexer::{Lexer, Pos, Token}; @@ -8,14 +9,15 @@ type Result = std::result::Result; /// Parse the top-level ISLE definitions and return their AST. pub fn parse(lexer: Lexer) -> Result> { - let parser = Parser::new(lexer); - parser.parse_defs() + let mut parser = Parser::new(lexer); + let result = parser.parse_defs()?; + Ok(result) } /// Parse without positional information. Provided mainly to support testing, to /// enable equality testing on structure alone. pub fn parse_without_pos(lexer: Lexer) -> Result> { - let parser = Parser::new_without_pos_tracking(lexer); + let mut parser = Parser::new_without_pos_tracking(lexer); parser.parse_defs() } @@ -23,7 +25,7 @@ pub fn parse_without_pos(lexer: Lexer) -> Result> { /// /// Takes in a lexer and creates an AST. #[derive(Clone, Debug)] -struct Parser<'a> { +pub struct Parser<'a> { lexer: Lexer<'a>, disable_pos: bool, } @@ -168,7 +170,7 @@ impl<'a> Parser<'a> { } } - fn parse_defs(mut self) -> Result> { + fn parse_defs(&mut self) -> Result> { let mut defs = vec![]; while !self.lexer.eof() { defs.push(self.parse_def()?); @@ -183,7 +185,10 @@ impl<'a> Parser<'a> { "pragma" => Def::Pragma(self.parse_pragma()?), "type" => Def::Type(self.parse_type()?), "decl" => Def::Decl(self.parse_decl()?), + "attr" => Def::Attr(self.parse_attr()?), "spec" => Def::Spec(self.parse_spec()?), + "macro" => Def::SpecMacro(self.parse_spec_macro()?), + "state" => Def::State(self.parse_state()?), "model" => Def::Model(self.parse_model()?), "form" => Def::Form(self.parse_form()?), "instantiate" => Def::Instantiation(self.parse_instantiation()?), @@ -396,6 +401,43 @@ impl<'a> Parser<'a> { }) } + fn parse_attr(&mut self) -> Result { + let pos = self.pos(); + let rule = self.eat_sym_str("rule")?; + let name = self.parse_ident()?; + let target = if rule { + AttrTarget::Rule(name) + } else { + AttrTarget::Term(name) + }; + let mut kinds = Vec::new(); + while !self.is_rparen() { + kinds.push(self.parse_attr_kind()?); + } + Ok(Attr { target, kinds, pos }) + } + + fn parse_attr_kind(&mut self) -> Result { + self.expect_lparen()?; + let pos = self.pos(); + let kind = match &self.expect_symbol()?[..] { + "veri" => self.parse_attr_kind_veri()?, + "tag" => AttrKind::Tag(self.parse_ident()?), + x => return Err(self.error(pos, format!("Not a valid attribute: {x}"))), + }; + self.expect_rparen()?; + Ok(kind) + } + + fn parse_attr_kind_veri(&mut self) -> Result { + let pos = self.pos(); + match &self.expect_symbol()?[..] { + "chain" => Ok(AttrKind::Chain), + "priority" => Ok(AttrKind::Priority), + x => Err(self.error(pos, format!("Not a valid verification attribute: {x}"))), + } + } + fn parse_spec(&mut self) -> Result { let pos = self.pos(); self.expect_lparen()?; // term with args: (spec ( ) (provide ...) ...) @@ -406,43 +448,80 @@ impl<'a> Parser<'a> { } self.expect_rparen()?; // end term with args - self.expect_lparen()?; // provide - if !self.eat_sym_str("provide")? { - return Err(self.error( - pos, - "Invalid spec: expected (spec ( ) (provide ...) ...)".to_string(), - )); - }; - let mut provides = vec![]; - while !self.is_rparen() { - provides.push(self.parse_spec_expr()?); - } - self.expect_rparen()?; // end provide - - let requires = if self.is_lparen() { + let mut provides = Vec::new(); + let mut requires = Vec::new(); + let mut matches = Vec::new(); + let mut modifies = Vec::new(); + while self.is_lparen() { self.expect_lparen()?; - if !self.eat_sym_str("require")? { - return Err(self.error( - pos, - "Invalid spec: expected (spec ( ) (provide ...) (require ...))" - .to_string(), - )); - } - let mut require = vec![]; - while !self.is_rparen() { - require.push(self.parse_spec_expr()?); + match &self.expect_symbol()?[..] { + "provide" => { + while !self.is_rparen() { + provides.push(self.parse_spec_expr()?); + } + } + "require" => { + while !self.is_rparen() { + requires.push(self.parse_spec_expr()?); + } + } + "match" => { + while !self.is_rparen() { + matches.push(self.parse_spec_expr()?); + } + } + "modifies" => { + let state = self.parse_ident()?; + let cond = if self.is_sym() { + Some(self.parse_ident().map_err(|err| { + self.error(pos, format!("Invalid modifies condition: {err:?}")) + })?) + } else { + None + }; + modifies.push(Modifies { state, cond }); + } + field => { + return Err(self.error( + pos, + format!("Invalid spec: unexpected field {field}. Expect (provide ...), (require ...) or (match ...)"), + )); + } } - self.expect_rparen()?; // end provide - require - } else { - vec![] - }; + self.expect_rparen()?; + } Ok(Spec { term, args, provides, requires, + matches, + modifies, + pos, + }) + } + + fn parse_spec_macro(&mut self) -> Result { + let pos = self.pos(); + + // Signature. + self.expect_lparen()?; + let name = self.parse_ident()?; + let mut params = vec![]; + while !self.is_rparen() { + params.push(self.parse_ident()?); + } + self.expect_rparen()?; + + // Body. + let body = self.parse_spec_expr()?; + + Ok(SpecMacro { + name, + params, + body, + pos, }) } @@ -450,18 +529,18 @@ impl<'a> Parser<'a> { let pos = self.pos(); if self.is_spec_bit_vector() { let (val, width) = self.parse_spec_bit_vector()?; - return Ok(SpecExpr::ConstBitVec { val, width, pos }); + Ok(SpecExpr::ConstBitVec { val, width, pos }) } else if self.is_int() { - return Ok(SpecExpr::ConstInt { + Ok(SpecExpr::ConstInt { val: self.expect_int()?, pos, - }); + }) } else if self.is_spec_bool() { let val = self.parse_spec_bool()?; - return Ok(SpecExpr::ConstBool { val, pos }); + Ok(SpecExpr::ConstBool { val, pos }) } else if self.is_sym() { let var = self.parse_ident()?; - return Ok(SpecExpr::Var { var, pos }); + Ok(SpecExpr::Var { var, pos }) } else if self.is_lparen() { self.expect_lparen()?; if self.eat_sym_str("switch")? { @@ -469,41 +548,126 @@ impl<'a> Parser<'a> { args.push(self.parse_spec_expr()?); while !(self.is_rparen()) { self.expect_lparen()?; + let pos = self.pos(); let l = Box::new(self.parse_spec_expr()?); let r = Box::new(self.parse_spec_expr()?); self.expect_rparen()?; - args.push(SpecExpr::Pair { l, r }); + args.push(SpecExpr::Pair { l, r, pos }); } self.expect_rparen()?; - return Ok(SpecExpr::Op { + Ok(SpecExpr::Op { op: SpecOp::Switch, args, pos, - }); - } - if self.is_sym() && !self.is_spec_bit_vector() { + }) + } else if self.eat_sym_str("let")? { + let mut defs = Vec::new(); + self.expect_lparen()?; + while !(self.is_rparen()) { + self.expect_lparen()?; + let ident = self.parse_ident()?; + let x = self.parse_spec_expr()?; + self.expect_rparen()?; + defs.push((ident, x)); + } + self.expect_rparen()?; + let body = Box::new(self.parse_spec_expr()?); + self.expect_rparen()?; + Ok(SpecExpr::Let { defs, body, pos }) + } else if self.eat_sym_str("with")? { + let mut decls = Vec::new(); + self.expect_lparen()?; + while !(self.is_rparen()) { + let ident = self.parse_ident()?; + decls.push(ident); + } + self.expect_rparen()?; + let body = Box::new(self.parse_spec_expr()?); + self.expect_rparen()?; + Ok(SpecExpr::With { decls, body, pos }) + } else if self.eat_sym_str("match")? { + let x = Box::new(self.parse_spec_expr()?); + let mut arms = Vec::new(); + while !(self.is_rparen()) { + let arm = self.parse_arm()?; + arms.push(arm); + } + self.expect_rparen()?; + Ok(SpecExpr::Match { x, arms, pos }) + } else if self.eat_sym_str("struct")? { + let mut fields = Vec::new(); + while !(self.is_rparen()) { + let field = self.parse_field_init()?; + fields.push(field); + } + self.expect_rparen()?; + Ok(SpecExpr::Struct { fields, pos }) + } else if self.eat_sym_str("macro")? { + self.expect_lparen()?; + let mut params = vec![]; + while !self.is_rparen() { + params.push(self.parse_ident()?); + } + self.expect_rparen()?; + let body = Box::new(self.parse_spec_expr()?); + self.expect_rparen()?; + Ok(SpecExpr::Macro { params, body, pos }) + } else if self.eat_sym_str("as")? { + let x = Box::new(self.parse_spec_expr()?); + let ty = self.parse_model_type()?; + self.expect_rparen()?; + Ok(SpecExpr::As { x, ty, pos }) + } else if self.is_sym() && !self.is_spec_bit_vector() { + let sym_pos = self.pos(); let sym = self.expect_symbol()?; - if let Ok(op) = self.parse_spec_op(sym.as_str()) { + if let Some(variant) = sym.strip_suffix('?') { + let variant = self.str_to_ident(sym_pos, variant)?; + let x = Box::new(self.parse_spec_expr()?); + self.expect_rparen()?; + Ok(SpecExpr::Discriminator { variant, x, pos }) + } else if let Some(name) = sym.strip_suffix('!') { + let name = self.str_to_ident(sym_pos, name)?; let mut args: Vec = vec![]; while !self.is_rparen() { args.push(self.parse_spec_expr()?); } self.expect_rparen()?; - return Ok(SpecExpr::Op { op, args, pos }); - }; - let ident = self.str_to_ident(pos, &sym)?; - if self.is_rparen() { + Ok(SpecExpr::Expand { name, args, pos }) + } else if let Ok(op) = self.parse_spec_op(sym.as_str()) { + let mut args: Vec = vec![]; + while !self.is_rparen() { + args.push(self.parse_spec_expr()?); + } self.expect_rparen()?; - return Ok(SpecExpr::Enum { name: ident }); - }; - } - // Unit - if self.is_rparen() { - self.expect_rparen()?; - return Ok(SpecExpr::ConstUnit { pos }); + Ok(SpecExpr::Op { op, args, pos }) + } else if let Some(field) = sym.strip_prefix(':') { + let field = self.str_to_ident(sym_pos, field)?; + let x = Box::new(self.parse_spec_expr()?); + self.expect_rparen()?; + Ok(SpecExpr::Field { field, x, pos }) + } else if let Some((name, variant)) = sym.split_once('.') { + let name = self.str_to_ident(pos, &name)?; + let variant = self.str_to_ident(pos, &variant)?; + let mut args: Vec = vec![]; + while !self.is_rparen() { + args.push(self.parse_spec_expr()?); + } + self.expect_rparen()?; + Ok(SpecExpr::Enum { + name, + variant, + args, + pos, + }) + } else { + Err(self.error(pos, "Unexpected spec expression".into())) + } + } else { + Err(self.error(pos, "Unexpected spec expression".into())) } + } else { + Err(self.error(pos, "Unexpected spec expression".into())) } - Err(self.error(pos, "Unexpected spec expression".into())) } fn parse_spec_op(&mut self, s: &str) -> Result { @@ -514,6 +678,9 @@ impl<'a> Parser<'a> { "not" => Ok(SpecOp::Not), "=>" => Ok(SpecOp::Imp), "or" => Ok(SpecOp::Or), + "+" => Ok(SpecOp::Add), + "-" => Ok(SpecOp::Sub), + "*" => Ok(SpecOp::Mul), "<=" => Ok(SpecOp::Lte), "<" => Ok(SpecOp::Lt), ">=" => Ok(SpecOp::Gte), @@ -548,34 +715,94 @@ impl<'a> Parser<'a> { "zero_ext" => Ok(SpecOp::ZeroExt), "sign_ext" => Ok(SpecOp::SignExt), "concat" => Ok(SpecOp::Concat), + "replicate" => Ok(SpecOp::Replicate), "conv_to" => Ok(SpecOp::ConvTo), "int2bv" => Ok(SpecOp::Int2BV), - "bv2int" => Ok(SpecOp::BV2Int), + "bv2nat" => Ok(SpecOp::BV2Nat), "widthof" => Ok(SpecOp::WidthOf), "if" => Ok(SpecOp::If), "switch" => Ok(SpecOp::Switch), - "subs" => Ok(SpecOp::Subs), "popcnt" => Ok(SpecOp::Popcnt), "rev" => Ok(SpecOp::Rev), "cls" => Ok(SpecOp::Cls), "clz" => Ok(SpecOp::Clz), - "load_effect" => Ok(SpecOp::LoadEffect), - "store_effect" => Ok(SpecOp::StoreEffect), + "to_fp" => Ok(SpecOp::ToFP), + "fp.to_ubv" => Ok(SpecOp::FPToUBV), + "fp.to_sbv" => Ok(SpecOp::FPToSBV), + "to_fp_unsigned" => Ok(SpecOp::ToFPUnsigned), + "to_fp_from_fp" => Ok(SpecOp::ToFPFromFP), + "fp.+oo" => Ok(SpecOp::FPPositiveInfinity), + "fp.-oo" => Ok(SpecOp::FPNegativeInfinity), + "fp.+zero" => Ok(SpecOp::FPPositiveZero), + "fp.-zero" => Ok(SpecOp::FPNegativeZero), + "fp.NaN" => Ok(SpecOp::FPNaN), + "fp.eq" => Ok(SpecOp::FPEq), + "fp.ne" => Ok(SpecOp::FPNe), + "fp.lt" => Ok(SpecOp::FPLt), + "fp.gt" => Ok(SpecOp::FPGt), + "fp.le" => Ok(SpecOp::FPLe), + "fp.ge" => Ok(SpecOp::FPGe), + "fp.add" => Ok(SpecOp::FPAdd), + "fp.sub" => Ok(SpecOp::FPSub), + "fp.mul" => Ok(SpecOp::FPMul), + "fp.div" => Ok(SpecOp::FPDiv), + "fp.min" => Ok(SpecOp::FPMin), + "fp.max" => Ok(SpecOp::FPMax), + "fp.neg" => Ok(SpecOp::FPNeg), + "fp.ceil" => Ok(SpecOp::FPCeil), + "fp.floor" => Ok(SpecOp::FPFloor), + "fp.sqrt" => Ok(SpecOp::FPSqrt), + "fp.trunc" => Ok(SpecOp::FPTrunc), + "fp.nearest" => Ok(SpecOp::FPNearest), + "fp.isZero" => Ok(SpecOp::FPIsZero), + "fp.isInfinite" => Ok(SpecOp::FPIsInfinite), + "fp.isNaN" => Ok(SpecOp::FPIsNaN), + "fp.isNegative" => Ok(SpecOp::FPIsNegative), + "fp.isPositive" => Ok(SpecOp::FPIsPositive), x => Err(self.error(pos, format!("Not a valid spec operator: {x}"))), } } - fn parse_spec_bit_vector(&mut self) -> Result<(i128, i8)> { + fn parse_arm(&mut self) -> Result { + self.expect_lparen()?; + let pos = self.pos(); + self.expect_lparen()?; + let variant = self.parse_ident()?; + let mut args = Vec::new(); + while !self.is_rparen() { + args.push(self.parse_ident()?); + } + self.expect_rparen()?; + let body = self.parse_spec_expr()?; + self.expect_rparen()?; + Ok(Arm { + variant, + args, + body, + pos, + }) + } + + fn parse_field_init(&mut self) -> Result { + self.expect_lparen()?; + let pos = self.pos(); + let name = self.parse_ident()?; + let value = Box::new(self.parse_spec_expr()?); + self.expect_rparen()?; + Ok(FieldInit { name, value, pos }) + } + + fn parse_spec_bit_vector(&mut self) -> Result<(u128, usize)> { let pos = self.pos(); let s = self.expect_symbol()?; if let Some(s) = s.strip_prefix("#b") { - match i128::from_str_radix(s, 2) { - Ok(i) => Ok((i, s.len() as i8)), + match u128::from_str_radix(s, 2) { + Ok(i) => Ok((i, s.len())), Err(_) => Err(self.error(pos, "Not a constant binary bit vector".to_string())), } } else if let Some(s) = s.strip_prefix("#x") { - match i128::from_str_radix(s, 16) { - Ok(i) => Ok((i, (s.len() as i8) * 4)), + match u128::from_str_radix(s, 16) { + Ok(i) => Ok((i, s.len() * 4)), Err(_) => Err(self.error(pos, "Not a constant hex bit vector".to_string())), } } else { @@ -602,53 +829,13 @@ impl<'a> Parser<'a> { let name = self.parse_ident()?; self.expect_lparen()?; // body let val = if self.eat_sym_str("type")? { - let ty = self.parse_model_type(); - ModelValue::TypeValue(ty?) - } else if self.eat_sym_str("enum")? { - let mut variants = vec![]; - let mut has_explicit_value = false; - let mut implicit_idx = None; - - while !self.is_rparen() { - self.expect_lparen()?; // enum value - let name = self.parse_ident()?; - let val = if self.is_rparen() { - // has implicit enum value - if has_explicit_value { - return Err(self.error( - pos, - format!( - "Spec enum has unexpected implicit value after implicit value." - ), - )); - } - implicit_idx = Some(if let Some(idx) = implicit_idx { - idx + 1 - } else { - 0 - }); - SpecExpr::ConstInt { - val: implicit_idx.unwrap(), - pos, - } - } else { - if implicit_idx.is_some() { - return Err(self.error( - pos, - format!( - "Spec enum has unexpected explicit value after implicit value." - ), - )); - } - has_explicit_value = true; - self.parse_spec_expr()? - }; - self.expect_rparen()?; - variants.push((name, val)); - } - ModelValue::EnumValues(variants) + let ty = self.parse_model_type()?; + ModelValue::TypeValue(ty) + } else if self.eat_sym_str("const")? { + let val = self.parse_spec_expr()?; + ModelValue::ConstValue(val) } else { - return Err(self.error(pos, "Model must be a type or enum".to_string())); + return Err(self.error(pos, "Model must be a type or const".to_string())); }; self.expect_rparen()?; // end body @@ -657,7 +844,11 @@ impl<'a> Parser<'a> { fn parse_model_type(&mut self) -> Result { let pos = self.pos(); - if self.eat_sym_str("Bool")? { + if self.eat_sym_str("!")? { + Ok(ModelType::Unspecified) + } else if self.eat_sym_str("_")? { + Ok(ModelType::Auto) + } else if self.eat_sym_str("Bool")? { Ok(ModelType::Bool) } else if self.eat_sym_str("Int")? { Ok(ModelType::Int) @@ -665,8 +856,8 @@ impl<'a> Parser<'a> { Ok(ModelType::Unit) } else if self.is_lparen() { self.expect_lparen()?; - let width = if self.eat_sym_str("bv")? { - if self.is_rparen() { + if self.eat_sym_str("bv")? { + let width = if self.is_rparen() { None } else if self.is_int() { Some(usize::try_from(self.expect_int()?).map_err(|err| { @@ -674,20 +865,62 @@ impl<'a> Parser<'a> { })?) } else { return Err(self.error(pos, "Badly formed BitVector (bv ...)".to_string())); + }; + self.expect_rparen()?; + Ok(ModelType::BitVec(width)) + } else if self.eat_sym_str("struct")? { + let mut fields = Vec::new(); + while !self.is_rparen() { + self.expect_lparen()?; + let name = self.parse_ident()?; + let ty = self.parse_model_type()?; + self.expect_rparen()?; + fields.push(ModelField { name, ty }); } + self.expect_rparen()?; + Ok(ModelType::Struct(fields)) + } else if self.eat_sym_str("named")? { + let name = self.parse_ident()?; + self.expect_rparen()?; + Ok(ModelType::Named(name)) } else { - return Err(self.error(pos, "Badly formed BitVector (bv ...)".to_string())); - }; - self.expect_rparen()?; - Ok(ModelType::BitVec(width)) + Err(self.error( + pos, + "Badly formed model: should be BitVector (bv ...) or Struct (struct ...)" + .to_string(), + )) + } } else { Err(self.error( pos, - "Model type be a Bool, Int, or BitVector (bv ...)".to_string(), + "Model type be a Bool, Int, BitVector (bv ...) or Struct (struct ...)".to_string(), )) } } + fn parse_state(&mut self) -> Result { + let pos = self.pos(); + let name = self.parse_ident()?; + let ty = self.parse_tagged_type("type")?; + + self.expect_lparen()?; + if !self.eat_sym_str("default")? { + return Err(self.error( + self.pos(), + format!("Invalid default: expected (default )"), + )); + }; + let default = self.parse_spec_expr()?; + self.expect_rparen()?; + + Ok(State { + name, + ty, + default, + pos, + }) + } + fn parse_form(&mut self) -> Result { let pos = self.pos(); let name = self.parse_ident()?; @@ -712,14 +945,8 @@ impl<'a> Parser<'a> { let pos = self.pos(); let args = self.parse_tagged_types("args")?; let ret = self.parse_tagged_type("ret")?; - let canonical = self.parse_tagged_type("canon")?; self.expect_rparen()?; - Ok(Signature { - args, - ret, - canonical, - pos, - }) + Ok(Signature { args, ret, pos }) } fn parse_tagged_types(&mut self, tag: &str) -> Result> { diff --git a/cranelift/isle/isle/src/printer.rs b/cranelift/isle/isle/src/printer.rs index a23b9053cc56..99383b7d6e78 100644 --- a/cranelift/isle/isle/src/printer.rs +++ b/cranelift/isle/isle/src/printer.rs @@ -16,6 +16,11 @@ pub fn print(defs: &[Def], width: usize, out: &mut W) -> std::io::Resu Ok(()) } +/// Dump a single ISLE node to standard output. +pub fn dump(node: &N) -> std::io::Result<()> { + print_node(node, 120, &mut std::io::stdout()) +} + /// Print a single ISLE node. pub fn print_node( node: &N, @@ -179,6 +184,9 @@ impl ToSExpr for Def { Def::Instantiation(instantiation) => instantiation.to_sexpr(), Def::Extern(ext) => ext.to_sexpr(), Def::Converter(converter) => converter.to_sexpr(), + Def::Attr(attr) => attr.to_sexpr(), + Def::SpecMacro(spec_macro) => spec_macro.to_sexpr(), + Def::State(state) => state.to_sexpr(), } } } @@ -285,6 +293,9 @@ impl ToSExpr for Spec { args, provides, requires, + matches, + modifies, + pos: _, } = self; let mut sig = vec![term.to_sexpr()]; sig.extend(args.iter().map(ToSExpr::to_sexpr)); @@ -292,10 +303,27 @@ impl ToSExpr for Spec { let mut parts = vec![SExpr::atom("spec")]; parts.push(SExpr::List(sig)); if !provides.is_empty() { - parts.push(SExpr::tagged("provide", &self.provides)); + parts.push(SExpr::tagged("provide", provides)); } if !requires.is_empty() { - parts.push(SExpr::tagged("require", &self.requires)); + parts.push(SExpr::tagged("require", requires)); + } + if !matches.is_empty() { + parts.push(SExpr::tagged("match", matches)); + } + for modifies in modifies { + parts.push(modifies.to_sexpr()); + } + SExpr::List(parts) + } +} + +impl ToSExpr for Modifies { + fn to_sexpr(&self) -> SExpr { + let Modifies { state, cond } = self; + let mut parts = vec![SExpr::atom("modifies"), state.to_sexpr()]; + if let Some(cond) = cond { + parts.push(cond.to_sexpr()); } SExpr::List(parts) } @@ -461,13 +489,7 @@ impl ToSExpr for ModelValue { fn to_sexpr(&self) -> SExpr { match self { ModelValue::TypeValue(mt) => SExpr::List(vec![SExpr::atom("type"), mt.to_sexpr()]), - ModelValue::EnumValues(enumerators) => { - let mut parts = vec![SExpr::atom("enum")]; - for (variant, value) in enumerators { - parts.push(SExpr::List(vec![variant.to_sexpr(), value.to_sexpr()])); - } - SExpr::List(parts) - } + ModelValue::ConstValue(e) => SExpr::List(vec![SExpr::atom("const"), e.to_sexpr()]), } } } @@ -482,22 +504,24 @@ impl ToSExpr for ModelType { SExpr::List(vec![SExpr::atom("bv"), SExpr::atom(size)]) } ModelType::BitVec(None) => SExpr::List(vec![SExpr::atom("bv")]), + ModelType::Struct(fields) => { + let mut parts = vec![SExpr::atom("struct")]; + parts.extend(fields.iter().map(ToSExpr::to_sexpr)); + SExpr::List(parts) + } + ModelType::Named(id) => SExpr::List(vec![SExpr::atom("named"), id.to_sexpr()]), + ModelType::Unspecified => SExpr::atom("!"), + ModelType::Auto => SExpr::atom("_"), } } } impl ToSExpr for Signature { fn to_sexpr(&self) -> SExpr { - let Signature { - args, - ret, - canonical, - pos: _, - } = self; + let Signature { args, ret, pos: _ } = self; SExpr::List(vec![ SExpr::tagged("args", args), SExpr::tagged("ret", std::slice::from_ref(ret)), - SExpr::tagged("canon", std::slice::from_ref(canonical)), ]) } } @@ -507,20 +531,84 @@ impl ToSExpr for SpecExpr { match self { SpecExpr::ConstInt { val, pos: _ } => SExpr::atom(val), SpecExpr::ConstBitVec { val, width, pos: _ } => SExpr::atom(if *width % 4 == 0 { - format!("#x{val:0width$x}", width = *width as usize / 4) + format!("#x{val:0width$x}", width = *width / 4) } else { - format!("#b{val:0width$b}", width = *width as usize) + format!("#b{val:0width$b}", width = *width) }), SpecExpr::ConstBool { val, pos: _ } => SExpr::atom(if *val { "true" } else { "false" }), - SpecExpr::ConstUnit { pos: _ } => SExpr::List(Vec::new()), SpecExpr::Var { var, pos: _ } => var.to_sexpr(), SpecExpr::Op { op, args, pos: _ } => { let mut parts = vec![op.to_sexpr()]; parts.extend(args.iter().map(ToSExpr::to_sexpr)); SExpr::List(parts) } - SpecExpr::Pair { l, r } => SExpr::List(vec![l.to_sexpr(), r.to_sexpr()]), - SpecExpr::Enum { name } => SExpr::List(vec![name.to_sexpr()]), + SpecExpr::As { x, ty, pos: _ } => { + SExpr::List(vec![SExpr::atom("as"), x.to_sexpr(), ty.to_sexpr()]) + } + SpecExpr::Field { field, x, pos: _ } => { + SExpr::List(vec![SExpr::atom(format!(":{}", field.0)), x.to_sexpr()]) + } + SpecExpr::Discriminator { variant, x, pos: _ } => { + SExpr::List(vec![SExpr::atom(format!("{}?", variant.0)), x.to_sexpr()]) + } + SpecExpr::Match { x, arms, pos: _ } => { + let mut parts = vec![SExpr::atom("match"), x.to_sexpr()]; + parts.extend(arms.iter().map(ToSExpr::to_sexpr)); + SExpr::List(parts) + } + SpecExpr::Let { defs, body, pos: _ } => { + let defs = defs + .iter() + .map(|(name, expr)| SExpr::List(vec![name.to_sexpr(), expr.to_sexpr()])) + .collect::>(); + + SExpr::List(vec![SExpr::atom("let"), SExpr::List(defs), body.to_sexpr()]) + } + SpecExpr::With { + decls, + body, + pos: _, + } => { + let decls = decls.iter().map(ToSExpr::to_sexpr).collect::>(); + SExpr::List(vec![ + SExpr::atom("with"), + SExpr::List(decls), + body.to_sexpr(), + ]) + } + SpecExpr::Macro { + params, + body, + pos: _, + } => { + let params = params.iter().map(ToSExpr::to_sexpr).collect::>(); + SExpr::List(vec![ + SExpr::atom("macro"), + SExpr::List(params), + body.to_sexpr(), + ]) + } + SpecExpr::Expand { name, args, pos: _ } => { + let mut parts = vec![SExpr::atom(format!("{}!", name.0))]; + parts.extend(args.iter().map(ToSExpr::to_sexpr)); + SExpr::List(parts) + } + SpecExpr::Pair { l, r, pos: _ } => SExpr::List(vec![l.to_sexpr(), r.to_sexpr()]), + SpecExpr::Enum { + name, + variant, + args, + pos: _, + } => { + let mut parts = vec![SExpr::atom(format!("{}.{}", name.0, variant.0))]; + parts.extend(args.iter().map(ToSExpr::to_sexpr)); + SExpr::List(parts) + } + SpecExpr::Struct { fields, pos: _ } => { + let mut parts = vec![SExpr::atom("struct")]; + parts.extend(fields.iter().map(ToSExpr::to_sexpr)); + SExpr::List(parts) + } } } } @@ -533,6 +621,9 @@ impl ToSExpr for SpecOp { SpecOp::Not => "not", SpecOp::Imp => "=>", SpecOp::Or => "or", + SpecOp::Add => "+", + SpecOp::Sub => "-", + SpecOp::Mul => "*", SpecOp::Lte => "<=", SpecOp::Lt => "<", SpecOp::Gte => ">=", @@ -567,8 +658,15 @@ impl ToSExpr for SpecOp { SpecOp::ZeroExt => "zero_ext", SpecOp::SignExt => "sign_ext", SpecOp::Concat => "concat", + SpecOp::Replicate => "replicate", SpecOp::ConvTo => "conv_to", SpecOp::Int2BV => "int2bv", + SpecOp::BV2Nat => "bv2nat", + SpecOp::ToFP => "to_fp", + SpecOp::FPToUBV => "fp.to_ubv", + SpecOp::FPToSBV => "fp.to_sbv", + SpecOp::ToFPUnsigned => "to_fp_unsigned", + SpecOp::ToFPFromFP => "to_fp_from_fp", SpecOp::WidthOf => "widthof", SpecOp::If => "if", SpecOp::Switch => "switch", @@ -576,10 +674,34 @@ impl ToSExpr for SpecOp { SpecOp::Rev => "rev", SpecOp::Cls => "cls", SpecOp::Clz => "clz", - SpecOp::Subs => "subs", - SpecOp::BV2Int => "bv2int", - SpecOp::LoadEffect => "load_effect", - SpecOp::StoreEffect => "store_effect", + SpecOp::FPPositiveInfinity => "fp.+oo", + SpecOp::FPNegativeInfinity => "fp.-oo", + SpecOp::FPPositiveZero => "fp.+zero", + SpecOp::FPNegativeZero => "fp.-zero", + SpecOp::FPNaN => "fp.NaN", + SpecOp::FPEq => "fp.eq", + SpecOp::FPNe => "fp.ne", + SpecOp::FPLt => "fp.lt", + SpecOp::FPGt => "fp.gt", + SpecOp::FPLe => "fp.le", + SpecOp::FPGe => "fp.ge", + SpecOp::FPAdd => "fp.add", + SpecOp::FPSub => "fp.sub", + SpecOp::FPMul => "fp.mul", + SpecOp::FPDiv => "fp.div", + SpecOp::FPMin => "fp.min", + SpecOp::FPMax => "fp.max", + SpecOp::FPNeg => "fp.neg", + SpecOp::FPCeil => "fp.ceil", + SpecOp::FPFloor => "fp.floor", + SpecOp::FPSqrt => "fp.sqrt", + SpecOp::FPTrunc => "fp.trunc", + SpecOp::FPNearest => "fp.nearest", + SpecOp::FPIsZero => "fp.isZero", + SpecOp::FPIsInfinite => "fp.isInfinite", + SpecOp::FPIsNaN => "fp.isNaN", + SpecOp::FPIsNegative => "fp.isNegative", + SpecOp::FPIsPositive => "fp.isPositive", }) } } @@ -670,3 +792,75 @@ impl ToSExpr for Ident { SExpr::atom(name.clone()) } } + +impl ToSExpr for AttrKind { + fn to_sexpr(&self) -> SExpr { + match self { + AttrKind::Chain => SExpr::List(vec![SExpr::atom("veri"), SExpr::atom("chain")]), + AttrKind::Priority => SExpr::List(vec![SExpr::atom("veri"), SExpr::atom("priority")]), + AttrKind::Tag(tag) => SExpr::List(vec![SExpr::atom("tag"), tag.to_sexpr()]), + } + } +} + +impl ToSExpr for Attr { + fn to_sexpr(&self) -> SExpr { + let mut parts = vec![SExpr::atom("attr")]; + match &self.target { + AttrTarget::Rule(name) => { + parts.push(SExpr::atom("rule")); + parts.push(name.to_sexpr()); + } + AttrTarget::Term(name) => { + parts.push(name.to_sexpr()); + } + } + parts.extend(self.kinds.iter().map(ToSExpr::to_sexpr)); + SExpr::List(parts) + } +} + +impl ToSExpr for SpecMacro { + fn to_sexpr(&self) -> SExpr { + let mut sig = vec![self.name.to_sexpr()]; + sig.extend(self.params.iter().map(ToSExpr::to_sexpr)); + + SExpr::List(vec![ + SExpr::atom("macro"), + SExpr::List(sig), + self.body.to_sexpr(), + ]) + } +} + +impl ToSExpr for State { + fn to_sexpr(&self) -> SExpr { + SExpr::List(vec![ + SExpr::atom("state"), + self.name.to_sexpr(), + SExpr::List(vec![SExpr::atom("type"), self.ty.to_sexpr()]), + SExpr::List(vec![SExpr::atom("default"), self.default.to_sexpr()]), + ]) + } +} + +impl ToSExpr for ModelField { + fn to_sexpr(&self) -> SExpr { + SExpr::List(vec![self.name.to_sexpr(), self.ty.to_sexpr()]) + } +} + +impl ToSExpr for FieldInit { + fn to_sexpr(&self) -> SExpr { + SExpr::List(vec![self.name.to_sexpr(), self.value.to_sexpr()]) + } +} + +impl ToSExpr for Arm { + fn to_sexpr(&self) -> SExpr { + let mut head = vec![self.variant.to_sexpr()]; + head.extend(self.args.iter().map(ToSExpr::to_sexpr)); + + SExpr::List(vec![SExpr::List(head), self.body.to_sexpr()]) + } +} diff --git a/cranelift/isle/isle/src/sema.rs b/cranelift/isle/isle/src/sema.rs index 27ff51413150..7029fb48234b 100644 --- a/cranelift/isle/isle/src/sema.rs +++ b/cranelift/isle/isle/src/sema.rs @@ -15,6 +15,7 @@ use crate::ast; use crate::error::*; +use crate::files::Files; use crate::lexer::Pos; use crate::log; use crate::stablemapset::{StableMap, StableSet}; @@ -191,7 +192,8 @@ impl BuiltinType { } } - const fn to_usize(&self) -> usize { + /// Get the built-in type's size. + pub const fn to_usize(&self) -> usize { match self { Self::Bool => 0, Self::Int(ty) => *ty as usize + 1, @@ -200,7 +202,8 @@ impl BuiltinType { } impl TypeId { - const fn builtin(builtin: BuiltinType) -> Self { + /// TypeId for builtin type. + pub const fn builtin(builtin: BuiltinType) -> Self { Self(builtin.to_usize()) } @@ -295,6 +298,14 @@ pub enum Type { } impl Type { + /// Get the ID of this `Type`. + pub fn id(&self) -> TypeId { + match self { + Self::Primitive(id, _, _) | Self::Enum { id, .. } | Self::Struct { id, .. } => *id, + Self::Builtin(b) => TypeId::builtin(*b), + } + } + /// Get the name of this `Type`. pub fn name<'a>(&self, tyenv: &'a TypeEnv) -> &'a str { match self { @@ -344,6 +355,9 @@ pub struct Variant { /// The data fields of this enum variant. pub fields: Fields, + + /// The ISLE source position where this variant is defined. + pub pos: Pos, } /// The fields of a struct or enum variant, formatted as a struct or tuple. @@ -422,6 +436,9 @@ pub struct TermEnv { /// This is indexed by `RuleId`. pub rules: Vec, + /// A map from an interned `Rule`'s name to its `RuleId`. + pub rule_map: StableMap, + /// Map from (inner_ty, outer_ty) pairs to term IDs, giving the /// defined implicit type-converter terms we can try to use to fit /// types together. @@ -657,27 +674,48 @@ impl Term { ) } + /// Is this term's constructor internal? + pub fn has_internal_constructor(&self) -> bool { + matches!( + self.kind, + TermKind::Decl { + constructor_kind: Some(ConstructorKind::InternalConstructor { .. }), + .. + } + ) + } + /// Get this term's extractor's external function signature, if any. pub fn extractor_sig(&self, tyenv: &TypeEnv) -> Option { match &self.kind { TermKind::Decl { flags, - extractor_kind: - Some(ExtractorKind::ExternalExtractor { - name, infallible, .. - }), + extractor_kind: Some(kind), .. } => { + let (func_name, full_name, infallible) = match kind { + ExtractorKind::InternalExtractor { .. } => { + let name = format!("extractor_{}", tyenv.syms[self.name.index()]); + (name.clone(), name, false) + } + ExtractorKind::ExternalExtractor { + name, infallible, .. + } => ( + tyenv.syms[name.index()].clone(), + format!("C::{}", tyenv.syms[name.index()]), + *infallible, + ), + }; let ret_kind = if flags.multi { ReturnKind::Iterator - } else if *infallible { + } else if infallible { ReturnKind::Plain } else { ReturnKind::Option }; Some(ExternalSig { - func_name: tyenv.syms[name.index()].clone(), - full_name: format!("C::{}", tyenv.syms[name.index()]), + func_name, + full_name, param_tys: vec![self.ret_ty], ret_tys: self.arg_tys.clone(), ret_kind, @@ -937,27 +975,28 @@ impl Pattern { } => { panic!("Pattern invocation of undefined term body") } - TermKind::Decl { - extractor_kind: Some(ExtractorKind::InternalExtractor { .. }), - .. - } => { - panic!("Should have been expanded away") - } TermKind::Decl { flags, - extractor_kind: Some(ExtractorKind::ExternalExtractor { infallible, .. }), + extractor_kind, .. } => { // Evaluate all `input` args. let output_tys = args.iter().map(|arg| arg.ty()).collect(); + let infallible = match extractor_kind { + Some(ExtractorKind::ExternalExtractor { infallible, .. }) => { + *infallible + } + _ => false, + }; + // Invoke the extractor. visitor.add_extract( input, termdata.ret_ty, output_tys, term, - *infallible && !flags.multi, + infallible && !flags.multi, flags.multi, ) } @@ -1188,6 +1227,14 @@ impl Rule { // Visit the rule's right-hand side, making use of the bound variables from the pattern. self.rhs.visit_in_rule(visitor, termenv, &vars) } + + /// Identifier is a name or position for referring to the rule. + pub fn identifier(&self, tyenv: &TypeEnv, files: &Files) -> String { + match self.name { + Some(sym) => tyenv.syms[sym.index()].clone(), + None => self.pos.pretty_print_line(files), + } + } } /// Given an `Option`, unwrap the inner `T` value, or `continue` if it is @@ -1341,8 +1388,7 @@ impl TypeEnv { let mut variants = vec![]; for variant in ty_variants { - let combined_ident = - ast::Ident(format!("{}.{}", ty.name.0, variant.name.0), variant.name.1); + let combined_ident = ast::Variant::full_name(&ty.name, &variant.name); let fullname = self.intern_mut(&combined_ident); let name = self.intern_mut(&variant.name); let id = VariantId(variants.len()); @@ -1359,6 +1405,7 @@ impl TypeEnv { fullname, id, fields, + pos: variant.pos, }); } Some(Type::Enum { @@ -1503,7 +1550,8 @@ impl TypeEnv { } } - fn intern(&self, ident: &ast::Ident) -> Option { + /// Lookup symbol ID for the given identifier. + pub fn intern(&self, ident: &ast::Ident) -> Option { self.sym_map.get(&ident.0).copied() } @@ -1513,6 +1561,15 @@ impl TypeEnv { .and_then(|sym| self.type_map.get(&sym)) .copied() } + + /// Lookup the term corresponding to the given enum variant. + pub fn get_variant(&self, ty: TypeId, variant: VariantId) -> &Variant { + let ty = &self.types[ty.index()]; + let Type::Enum { variants, .. } = ty else { + unreachable!("provided type must be an enum") + }; + &variants[variant.index()] + } } #[derive(Clone, Debug, Default)] @@ -1571,6 +1628,7 @@ impl TermEnv { terms: vec![], term_map: StableMap::new(), rules: vec![], + rule_map: StableMap::new(), converters: StableMap::new(), expand_internal_extractors, }; @@ -1698,7 +1756,7 @@ impl TermEnv { let ret_ty = id; self.terms.push(Term { id: tid, - decl_pos: pos, + decl_pos: variant.pos, name: variant.fullname, arg_tys, ret_ty, @@ -2228,13 +2286,51 @@ impl TermEnv { rhs, vars: bindings.seen, prio, - pos, name: rule.name.as_ref().map(|i| tyenv.intern_mut(i)), + pos, }); } _ => {} } } + + // Populate default rule names. + // + // Unnamed rules that are the only rule for their root term adopt the + // name of the root term. + let mut term_rule_count: HashMap = HashMap::new(); + for rule in &self.rules { + *term_rule_count.entry(rule.root_term).or_default() += 1; + } + + for rule in &mut self.rules { + if rule.name.is_none() + && term_rule_count + .get(&rule.root_term) + .copied() + .unwrap_or_default() + == 1 + { + let term = &self.terms[rule.root_term.index()]; + rule.name = Some(term.name); + } + } + + // Populate rule name map. + for rule in &self.rules { + let Some(name) = rule.name else { continue }; + match self.rule_map.entry(name) { + Entry::Vacant(e) => { + e.insert(rule.id); + } + Entry::Occupied(_) => { + tyenv.report_error( + rule.pos, + format!("Duplicate rule name: '{}'", tyenv.syms[name.index()]), + ); + } + } + } } fn check_for_undefined_decls(&self, tyenv: &mut TypeEnv, defs: &[ast::Def]) { @@ -2870,6 +2966,20 @@ impl TermEnv { .and_then(|sym| self.term_map.get(&sym)) .copied() } + + /// Lookup rule by name. + pub fn get_rule_by_name(&self, tyenv: &TypeEnv, sym: &ast::Ident) -> Option { + tyenv + .intern(sym) + .and_then(|sym| self.rule_map.get(&sym)) + .copied() + } + + /// Lookup the term corresponding to the given enum variant. + pub fn get_variant_term(&self, tyenv: &TypeEnv, ty: TypeId, variant: VariantId) -> TermId { + let variant = tyenv.get_variant(ty, variant); + self.term_map[&variant.fullname] + } } #[cfg(test)] @@ -2949,6 +3059,10 @@ mod test { }, ], }), + pos: Pos { + file: 0, + offset: 77, + }, }, Variant { name: sym_c, @@ -2961,6 +3075,10 @@ mod test { ty: TypeId::U32, }], }), + pos: Pos { + file: 0, + offset: 99, + }, }, ], pos: Pos { diff --git a/cranelift/isle/isle/src/stablemapset.rs b/cranelift/isle/isle/src/stablemapset.rs index 6b72d80c8b5a..b43c43c62ae1 100644 --- a/cranelift/isle/isle/src/stablemapset.rs +++ b/cranelift/isle/isle/src/stablemapset.rs @@ -28,6 +28,11 @@ impl StableSet { pub fn contains(&self, val: &T) -> bool { self.0.contains(val) } + + /// Returns the number of elements in the set. + pub fn len(&self) -> usize { + self.0.len() + } } /// A wrapper around a [HashMap] which prevents accidentally observing the non-deterministic diff --git a/cranelift/isle/isle/src/trie_again.rs b/cranelift/isle/isle/src/trie_again.rs index 1f1cd98cbbf4..dba33b060fd6 100644 --- a/cranelift/isle/isle/src/trie_again.rs +++ b/cranelift/isle/isle/src/trie_again.rs @@ -3,7 +3,7 @@ use crate::disjointsets::DisjointSets; use crate::error::{Error, Span}; use crate::lexer::Pos; -use crate::sema; +use crate::sema::{self, RuleId, TermEnv, TermId, TypeEnv}; use crate::stablemapset::StableSet; use std::collections::{HashMap, hash_map::Entry}; @@ -210,6 +210,8 @@ pub enum Constraint { /// contains this rule. #[derive(Debug, Default)] pub struct Rule { + /// Identifier of the source rule. + pub id: RuleId, /// Where was this rule defined? pub pos: Pos, /// All of these bindings must match the given constraints for this rule to apply. Note that @@ -223,6 +225,8 @@ pub struct Rule { /// If other rules apply along with this one, the one with the highest numeric priority is /// evaluated. If multiple applicable rules have the same priority, that's an overlap error. pub prio: i64, + /// Rule name. Used for tracing. + pub name: Option, /// If this rule applies, these side effects should be evaluated before returning. pub impure: Vec, /// If this rule applies, the top-level term should evaluate to this expression. @@ -304,6 +308,28 @@ impl Binding { Binding::MatchTuple { source, .. } => std::slice::from_ref(source), } } + + /// Returns the term referenced by this binding. + pub fn term(&self, tyenv: &TypeEnv, termenv: &TermEnv) -> Option { + match self { + Binding::ConstInt { .. } => None, + Binding::ConstBool { .. } => None, + Binding::ConstPrim { .. } => None, + Binding::Argument { .. } => None, + Binding::Extractor { term, .. } => Some(*term), + Binding::Constructor { term, .. } => Some(*term), + Binding::Iterator { .. } => None, + Binding::MakeVariant { ty, variant, .. } => { + Some(termenv.get_variant_term(tyenv, *ty, *variant)) + } + Binding::MatchVariant { .. } => None, + Binding::MakeStruct { .. } => None, + Binding::ExtractStruct { .. } => None, + Binding::MakeSome { .. } => None, + Binding::MatchSome { .. } => None, + Binding::MatchTuple { .. } => None, + } + } } impl Constraint { @@ -331,6 +357,28 @@ impl Constraint { .collect(), } } + + /// Determine if this constraint could be compatible with a given binding. + pub fn compatible(&self, binding: &Binding) -> bool { + match (self, binding) { + ( + Constraint::Variant { + ty: tc, + variant: vc, + .. + }, + Binding::MakeVariant { + ty: tb, + variant: vb, + .. + }, + ) => tb == tc && vb == vc, + (Constraint::ConstInt { val: vc, ty: tc }, Binding::ConstInt { val: vb, ty: tb }) => { + vc == vb && tc == tb + } + _ => true, + } + } } impl Rule { @@ -437,8 +485,10 @@ struct RuleSetBuilder { impl RuleSetBuilder { fn add_rule(&mut self, rule: &sema::Rule, termenv: &sema::TermEnv, errors: &mut Vec) { self.impure_instance = 0; + self.current_rule.id = rule.id; self.current_rule.pos = rule.pos; self.current_rule.prio = rule.prio; + self.current_rule.name = rule.name; self.current_rule.result = rule.visit(self, termenv); if termenv.terms[rule.root_term.index()].is_partial() { self.current_rule.result = self.dedup_binding(Binding::MakeSome { diff --git a/cranelift/isle/isle/tests/printer_tests.rs b/cranelift/isle/isle/tests/printer_tests.rs new file mode 100644 index 000000000000..e2a74ce590ad --- /dev/null +++ b/cranelift/isle/isle/tests/printer_tests.rs @@ -0,0 +1,33 @@ +//! Auto-generated ISLE printer tests. + +use cranelift_isle::lexer; +use cranelift_isle::parser; +use cranelift_isle::printer; +use std::io::BufWriter; +use std::iter::zip; + +pub fn run_print(isle_filename: &str) { + // Parse. + let original_src = std::fs::read_to_string(isle_filename).unwrap(); + let lexer = lexer::Lexer::new(0, &original_src).unwrap(); + let original = parser::parse_without_pos(lexer).unwrap(); + + // Print. + let mut buf = BufWriter::new(Vec::new()); + printer::print(&original, 78, &mut buf).unwrap(); + let bytes = buf.into_inner().unwrap(); + let printed_src = String::from_utf8(bytes).unwrap(); + + // Round trip. + let lexer = lexer::Lexer::new(0, &printed_src).unwrap(); + let round_trip = parser::parse_without_pos(lexer).unwrap(); + + // Ensure equal. + assert_eq!(original.len(), round_trip.len()); + for (orig, rt) in zip(original, round_trip) { + assert_eq!(orig, rt); + } +} + +// Generated by build.rs. +include!(concat!(env!("OUT_DIR"), "/isle_printer_tests.rs")); diff --git a/cranelift/isle/veri/README.md b/cranelift/isle/veri/README.md index 55074defc5b8..653e43e81821 100644 --- a/cranelift/isle/veri/README.md +++ b/cranelift/isle/veri/README.md @@ -1,287 +1,133 @@ -# Crocus: An SMT-based ISLE verification tool +# VeriISLE -This directory contains Crocus, a tool for verifying instruction lowering and transformation rules written in ISLE. Crocus uses an underlying SMT solver to model values in ISLE rules in as logical bitvectors, searching over all possible inputs to find potential soundness counterexamples. The motivation and context project are described in detail in our ASPLOS 2024 paper: [Lightweight, Modular Verification for WebAssembly-to-Native Instruction Selection](https://dl.acm.org/doi/10.1145/3617232.3624862). +VeriISLE is an in-development [SMT](https://smt-lib.org)-based verifier for the +[ISLE language](../docs/language-reference.md). -Currently[^1], Crocus requires every ISLE term uses within a rule to have a user-provided specification, or `spec`, that provides the logical preconditions and effects of the term (`require` and `provide` blocks). -The syntax for these specs is embedded as an optional extension to ISLE itself: specs are written in the ISLE source files. +It analyzes chains of ISLE rules, using a combination of hand-written `spec`s and +specifications derived from authoritative ISA semantics, such as [ASL](https://developer.arm.com/architectures/architecture%20specification%20language) for the `aarch64` backend. -[^1]: We have work in progress to lower this annotation burden. +The verification work is detailed in two academic papers: +- The most recent OOPSLA 2025 paper described the automatic rule chaining, authoritative ISA specification derivations, and our current state modeling approach: [Scaling Instruction-Selection Verification against Authoritative ISA Semantics](https://doi.org/10.1145/3764383). + Michael McLoughlin, Ashley Sheng, Chris Fallin, Bryan Parno, Fraser Brown, and + Alexa VanHattum. OOPSLA 2025. +- The earlier ASPLOS 2024 paper described the overall verification strategy and more bugs this work prevented and/or reproduced: [Lightweight, Modular Verification for WebAssembly-to-Native Instruction Selection](https://doi.org/10.1145/3617232.3624862). + Alexa VanHattum, Monica Pardeshi, Chris Fallin, Adrian Sampson, and Fraser + Brown. ASPLOS 2024. -## Running on an individual rule +## Dependencies -The easiest way to run Crocus on an individual ISLE rule is to give that rule a name. +To run the verifier you will need a backend SMT solver installed. The default +configuration uses both [cvc5](https://cvc5.github.io/) and +[z3](https://github.com/Z3Prover/z3): most expansions are checked with `cvc5`, +while expansions tagged `solver_z3` (for example floating-point operations) are +checked with `z3`. -For example, to verify the following `aarch64` rule: +On MacOS, you can install both via homebrew: ``` -(rule -1 (lower (has_type (fits_in_64 ty) (band x y))) - (alu_rs_imm_logic_commutative (ALUOp.And) ty x y)) +brew install cvc5/homebrew-cvc5/cvc5 +brew install z3 ``` -We can add a name (before the priority): -``` -(rule band_fits_in_64 -1 (lower (has_type (fits_in_64 ty) (band x y))) - (alu_rs_imm_logic_commutative (ALUOp.And) ty x y)) -``` - -We also require that the relevant (outermost) CLIF term on the left hand side has a "type instantiation" to specify the types, e.g. bitwidths, we are interested in verifying. In this case, this is provided with: +Alternatively, on Linux or MacOS you can install from Github release with: ``` -(form - bv_binary_8_to_64 - ((args (bv 8) (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16) (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 32) (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) - -(instantiate band bv_binary_8_to_64) +./veri/script/install/cvc5.sh -i +./veri/script/install/z3.sh -b /bin ``` +If you use this method, ensure that `/bin` is on your `$PATH`. -We can then invoke the rule with the following, using `-t` or `--term` to specify the relevant CLIF instruction and `--names` to specify the name of the rule: +## Running -``` -cargo run -- --codegen ../../../codegen --aarch64 -t band --names band_fits_in_64 -``` - -With the expected output: +To run the verifier, run: ``` -Writing generated file: /Users/avh/research/wasmtime/cranelift/isle/veri/veri_engine/output/clif_opt.isle -Writing generated file: /Users/avh/research/wasmtime/cranelift/isle/veri/veri_engine/output/clif_lower.isle -Verification succeeded for band_fits_in_64, width 8 -Verification succeeded for band_fits_in_64, width 16 -Verification succeeded for band_fits_in_64, width 32 -Verification succeeded for band_fits_in_64, width 64 +cargo run -p cranelift-isle-veri --bin veri -- --default-excludes ``` -If the rule was unsound, this will report counterexamples. For instance, if we change the rule to the following: +This will run verification on the default AArch64 backend. To run on the X64 +backend, add the `-a x64` option. `--default-excludes` will skip ISLE terms +that are either currently not well-supported or slow to verify, such as vector operations +and expensive division operations. -``` -(rule band_fits_in_64 -1 (lower (has_type (fits_in_64 ty) (band x y))) - (alu_rs_imm_logic_commutative (ALUOp.Or) ty x y)) -``` - -Then the output would include counterexamples, like so: - -``` -Verification failed for band_fits_in_64, width 8 -Counterexample summary -(lower (has_type (fits_in_64 [ty|8]) (band [x|#x01|0b00000001] [y|#x00|0b00000000]))) -=> -(output_reg (alu_rs_imm_logic_commutative (ALUOp.Orr) [ty|8] [x|#x01|0b00000001] [y|#x00|0b00000000])) - -#x00|0b00000000 => -#x01|0b00000001 +By default the verifier attempts every expansion it can reach. It seeds an +expansion at every term that has rules, a constructor, and an explicit +specification, and verifies all rule chains reachable from those roots. Terms +without a spec are not verified standalone; they are only checked when chained +(inlined) into a specified root. Expansions tagged `TODO` are skipped by default +(pass `--no-skip-todo` to include them). -Failed condition: -(= ((_ extract 7 0) lower__13) ((_ extract 7 0) output_reg__16)) -``` +### Filtering expansions -## The annotation language +During development you may want to focus on a subset of expansions. Pass one or +more `--filter` arguments, each of the form `[include:|exclude:]`. The +supported predicates are: -The annotation maps closely to [SMT-LIB](https://smt-lib.org) theories of bitvectors and booleans, with a several added conveniences. +| Predicate | Matches an expansion where... | +| ----------------- | ---------------------------------------------------------- | +| `tag:` | the root term, a rule, or any chained term carries `` | +| `root:` | the root term is `` | +| `rule:` | the expansion contains the named `` | +| `not:` | `` does not match | +| `

,` | both `

` and `` match (logical and) | -### Top-level constructs +Filters are evaluated in order and the **last** matching filter wins. Every +expansion is **included by default**, so a filter list behaves like a denylist: +`exclude:` filters narrow the set, while `include:` filters carve exceptions back +out of a preceding `exclude:`. A bare predicate with no prefix is treated as +`include:`. -We extend the ISLE parser with the following top-level constructs: +Because the default is to include everything, an `include:` filter only has an +effect when it follows an `exclude:` that would otherwise drop the expansion. To +*restrict* verification to expansions matching a predicate, exclude its negation. +For example, to focus on all expansions involving a given rule (first add a name +to the rule if it does not have one): -- `model` specifies how an ISLE type maps to an SMT type. For example, the follow ISLE type definitions along with their models specify how booleans and `u8`s are modeled: ``` -(model u8 (type (bv 8))) -(type u8 (primitive u8)) -(model bool (type Bool)) -(type bool (primitive bool)) +./script/veri.sh -- --filter exclude:not:rule: ``` -Models can be `Bool`, `Int`, or `(bv)` with or without a specific bitwidth. If the bitwidth is not provided, Crocus type inference will verify the rule with all possible inferred widths - -- As in the example above, `instantiate` and `form` specify what type instantiations should be considered for a verification. - -- `spec` terms provide specifications for ISLE declarations, which can correspond to ISLE instructions, ISA instructions, external constructors/extractors defined in Rust, or transient, ISLE-only terms. Specs take the form `(spec (term arg1 ... argN) (provide p1 ... pM) (require r1 ... rO))`, providing the `term` termname (must be a defined ISLE decl), fresh variables `arg1 ... argN` to refer to the arguments, and zero or more provide and require expressions `p1, ..., pN, r1, ..., RN` that take the form of expressions with operations as described below. `spec` terms use the keyword `result` to constrain the return value of the term. - -### General SMT-LIB operations - -The following terms exactly match their general SMT-LIB meaning: - -- `=`: equality -- `and`: boolean and -- `or`: boolean or -- `not`: boolean negation -- `=>`: boolean implication - -We additionally support variadic uses of the `and` and `or` operations (these desugar to the binary SMT-LIB versions as expected). - -### Integer operations - -The following terms exactly match the [SMT-LIB theories `Int`](https://smt-lib.org/theories-Ints.shtml). - -- `<` -- `<=` -- `>` -- `>=` - -In specs, integer operations are primarily used for comparing the number of bits in an ISLE type. - -### Bitvector operations - -The following terms exactly match [SMT-LIB theory `FixedSizeBitVectors`](https://smt-lib.org/theories-FixedSizeBitVectors.shtml). - -There operations are typically used in specs for any operations on ISLE `Value`s. +Similarly, `--filter exclude:not:root:` limits to a single root term. +Alternatively, `--only-root ` scopes expansion itself to one root rather +than filtering after the fact. -- `bvnot` -- `bvand` -- `bvor` -- `bvxor` -- `bvneg` -- `bvadd` -- `bvsub` -- `bvmul` -- `bvudiv` -- `bvurem` -- `bvsdiv` -- `bvsrem` -- `bvshl` -- `bvlshr` -- `bvashr` -- `bvsaddo` -- `bvule` -- `bvult` -- `bvugt` -- `bvuge` -- `bvslt` -- `bvsle` -- `bvsgt` -- `bvsge` +## ISA Specifications -### Custom bitvector operations +Where possible we derive ISA specifications in VeriISLE format from +authoritative specifications distributed by vendors. Currently this is only +in place for the AArch64 backend, with specifications derived from ARM's Machine +Readable Specification in Architecture Specification Language (ASL). We rely on +the [ASLp](https://github.com/UQ-PAC/aslp) tool to assist with distilling down +the original verbose specifications to usable semantics for verification. -- `int2bv`: equivalent to SMT-LIB `nat2bv`. -- `bv2int`: equivalent to SMT-LIB `bv2nat`. -- `extract`: `(extract h l e)` where `h` and `l` are integer literals and `e` is a bitvector is equivalent to SMT-LIB `((_ extract h l) e)`. -- `zero_ext`: `(zero_ext w e)` where `w : Int` and `e : (bv N)` is equivalent to SMT-LIB `((_ zero_extend M) e))` where `M = w - N`. -- `sign_ext`: `(sign_ext w e)` where `w : Int` and `e : (bv N)` is equivalent to SMT-LIB `((_ sign_extend M) e))` where `M = w - N`. -- `rotr`: `(rotr e1 e2)` where `e1, e2: (bv N)` resolves to `(bvor (bvlshr e1 e3) (bvshl e1 (bvsub (nat2bv N N) e3)))`, where `e3 = (bvurem e2 (nat2bv N N))`. Bitvector rotate right. -- `rotl`: `(rotl e1 e2)` where `e1, e2: (bv N)` resolves to `(bvor (bvshl e1 e3) (bvlshr e1 (bvsub (nat2bv N N) e3)))`, where `e3 = (bvurem e2 (nat2bv N N))`. Bitvector rotate left. -- `concat`: `(concat e_1... e_N)` resolves to `(concat e_1 (concat e_2 (concat ... e_N)))`. That is, this is a variadic version of the SMT-LIB `concat` operation. -- `widthof`: `(widthof e)` where `e : (bv N)` resolves to `N`. That is, returns the bitwidth of a supplied bitvector as an integer. -- `subs`: `(subs e1 e2)` returns the results of a subtraction with flags. -- `popcnt`: `(popcnt e)` where `e : (bv N)` returns the count of non-zero bits in `e`. -- `rev`: `(rev e)` where `e : (bv N)` reverses the order of bits in `e`. -- `cls`: `(cls e)` where `e : (bv N)` returns the count of leading sign bits in `e`. -- `clz`: `(clz e)` where `e : (bv N)` returns the count of leading zero bits in `e`. -- `convto`: `(convto w e)` where `w : Int` and `e : (bv N)` converts the bitvector `e` to the width `w`, leaving the upper bits unspecified in the case of a extension. That is, there are 3 cases: - 1. `w = N`: resolves to `e`. - 2. `w < N`: resolves to `((_ extract M 0) e)` where `M = N - 1`. - 3. `w > N`: resolves to `(concat e2 e)` where `e2` is a fresh bitvector with `w - N` unspecified bits. +The resulting ISA specifications are +[checked in to the repository](../../codegen/src/isa/aarch64/spec), so there is +no requirement to install ASLp unless you want to alter existing or derive more +specifications with it. -### Custom memory operations +### Generating ISA Specifications -- `load_effect`: `(load_effect flags size address)` where `flags : (bv 16)`, `size: Int`, and `address : (bv 64)` models a load of `size` bits from address `address` with flags `flags`. Only 1 `load_effect` may be used per left hand and right hand side of a rule. -- `store_effect`: `(store_effect flags size val address)` where `flags : (bv 16)`, `size: Int`, and `val : (bv size)`, `address : (bv 64)` models a store of `val` (with `size` bits) to address `address` with flags `flags`. Only 1 `store_effect` may be used per left hand and right hand side of a rule. +To run ISA specification generation, you will first need to install ASLp: -### Custom control operation +1. [Install `opam`](https://opam.ocaml.org/doc/Install.html), the OCaml Package + Manager. The "Binary distribution" method is recommended. +2. Install ASLp with `./veri/script/install/aslp.sh -i `. +3. Ensure ASLp tools are available by adding `/bin` to your + `PATH`. -- `if`: equivalent to SMT-LIB `ite`. -- `switch`: `(switch c (m1 e1) ... (mN eN))` resolves to a series of nested `ite` expressions, -`(ite(= c m1) e1 (ite (= c m2) e2 (ite ...eN)))`. It additionally adds a verification condition that some case must match, that is, `(or (= c m1) (or (= c m2)...(= c mN)))`. - -## Example - -Continuing the `band_fits_in_64` example from before, the full required specifications are places in the relevant ISLE files. +To run ISA specification generation, from the `isaspec` directory run: ``` -(rule band_fits_in_64 -1 (lower (has_type (fits_in_64 ty) (band x y))) - (alu_rs_imm_logic_commutative (ALUOp.And) ty x y)) +./script/generate.sh -l ``` -In `inst_specs.isle`: - -``` -;; The band spec uses the bitvector `bvand` on its arguments. -(spec (band x y) - (provide (= result (bvand x y)))) -(instantiate band bv_binary_8_to_64) -``` - -In `prelude_lower.isle`: - -``` -;; has_type checks that the integer modeling the type in matches the Inst bitwidth. -(spec (has_type ty arg) - (provide (= result arg)) - (require (= ty (widthof arg)))) -(decl has_type (Type Inst) Inst) - -;; fits_in_64 checks that the integer modeling the width is less than or equal to 64. -(spec (fits_in_64 arg) - (provide (= result arg)) - (require (<= arg 64))) -(decl fits_in_64 (Type) Type) -``` - -In `aarch64/lower.isle`: - -``` -;; lower is just modeled as an identity function -(spec (lower arg) (provide (= result arg))) -(decl partial lower (Inst) InstOutput) -``` +This will: -In `aarch64/inst.isle`: - -``` -;; Enum models ALUOp as an 8-bit bitvector. -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -;; alu_rs_imm_logic_commutative uses a conv_to and switch. -(spec (alu_rs_imm_logic_commutative op t a b) - (provide - (= result - (conv_to 64 - (switch op - ((ALUOp.Orr) (bvor a b)) - ((ALUOp.And) (bvand a b)) - ((ALUOp.Eor) (bvxor a b))))))) -(decl alu_rs_imm_logic_commutative (ALUOp Type Value Value) Reg) -``` - -## Testing - -To see an all of our current output, run tests without capturing standard out: -```bash -cargo test -- --nocapture -``` - -To run a specific test, you can provide the test name (most rules are tested in `cranelift/isle/veri/veri_engine/tests/veri.rs`). Set `RUST_LOG=DEBUG` to see more detailed output on test cases that expect success. - -```bash -RUST_LOG=DEBUG cargo test test_named_band_fits_in_64 -- --nocapture -``` - -To see the x86-64 CVE repro, run: - -```bash -RUST_LOG=debug cargo run -- --codegen ../../../codegen --noprelude -t amode_add -i examples/x86/amode_add_uextend_shl.isle -``` +1. Launch an instance of the `aslp-server`. Communicating with ASLp over a + server connection allows us to pay the initialization cost of reading the + large ASL specification once. +2. Build and execute the `isaspec` tool. +3. Write outputs to the `cranelift/codegen/src/isa/aarch64/spec/` directory. -To see the x86-64 CVE variant with a 32-bit address, run: -```bash -RUST_LOG=debug cargo run -- --codegen ../../../codegen --noprelude -t amode_add -i examples/x86/amode_add_shl.isle -``` \ No newline at end of file +On a clean checkout this should be a no-op. diff --git a/cranelift/isle/veri/aslp/Cargo.toml b/cranelift/isle/veri/aslp/Cargo.toml new file mode 100644 index 000000000000..9ec8b9ac3d77 --- /dev/null +++ b/cranelift/isle/veri/aslp/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cranelift-isle-veri-aslp" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +pest = { workspace = true } +pest_derive = { workspace = true } +enquote = { workspace = true } +clap = { workspace = true } +anyhow = { workspace = true, features = ['std', 'backtrace'] } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +cranelift-isle-veri-test-macros = { path = "../test-macros" } +reqwest = { workspace = true, features = ["blocking", "json"] } +serde = { workspace = true, features = ["derive"] } diff --git a/cranelift/isle/veri/aslp/src/aslt.pest b/cranelift/isle/veri/aslp/src/aslt.pest new file mode 100644 index 000000000000..d05f5ae6aa50 --- /dev/null +++ b/cranelift/isle/veri/aslp/src/aslt.pest @@ -0,0 +1,118 @@ +aslt = _{ SOI ~ (stmt ~ (NEWLINE ~ stmt)* ~ NEWLINE?)? ~ EOI } + +// Statements + +stmt = { + stmt_assign + | stmt_constdecl + | stmt_vardecl + | stmt_vardeclsnoinit + | stmt_assert + | stmt_if + | stmt_tcall +} + +stmt_assign = { "Stmt_Assign(" ~ lexpr ~ "," ~ expr ~ ")" } +stmt_constdecl = { "Stmt_ConstDecl(" ~ ty ~ "," ~ ident ~ "," ~ expr ~ ")" } +stmt_vardecl = { "Stmt_VarDecl(" ~ ty ~ "," ~ ident ~ "," ~ expr ~ ")" } +stmt_vardeclsnoinit = { "Stmt_VarDeclsNoInit(" ~ ty ~ "," ~ vars ~ ")" } +stmt_assert = { "Stmt_Assert(" ~ expr ~ ")" } +stmt_if = { "Stmt_If(" ~ expr ~ "," ~ stmts ~ "," ~ stmts ~ "," ~ stmts ~ ")" } +stmt_tcall = { "Stmt_TCall(" ~ func_ident ~ "," ~ exprs ~ "," ~ exprs ~ ")" } + +stmts = { "[" ~ (NEWLINE ~ stmt ~ (";" ~ NEWLINE ~ stmt)* ~ NEWLINE)? ~ "]" } + +// Left-hand-side Expressions + +lexpr = { + lexpr_array + | lexpr_field + | lexpr_var +} + +lexpr_array = { "LExpr_Array(" ~ lexpr ~ "," ~ expr ~ ")" } +lexpr_field = { "LExpr_Field(" ~ lexpr ~ "," ~ ident ~ ")" } +lexpr_var = { "LExpr_Var(" ~ var ~ ")" } + +// Expressions + +expr = { + expr_paren + | expr_array + | expr_field + | expr_var + | expr_tapply + | expr_slices + | expr_litint + | expr_litbits +} + +expr_paren = _{ "(" ~ expr ~ ")" } +expr_array = { "Expr_Array(" ~ expr ~ "," ~ expr ~ ")" } +expr_field = { "Expr_Field(" ~ expr ~ "," ~ ident ~ ")" } +expr_var = { "Expr_Var(" ~ var ~ ")" } +expr_tapply = { "Expr_TApply(" ~ func_ident ~ "," ~ exprs ~ "," ~ exprs ~ ")" } +expr_slices = { "Expr_Slices(" ~ expr ~ "," ~ slices ~ ")" } +expr_litint = { integer } +expr_litbits = { bits } + +exprs = { "[" ~ (expr ~ (";" ~ expr)*)? ~ "]" } + +// Slices + +slice = { + slice_paren + | slice_lowd +} + +slice_paren = _{ "(" ~ slice ~ ")" } +slice_lowd = { "Slice_LoWd(" ~ expr ~ "," ~ expr ~ ")" } + +slices = { "[" ~ (slice ~ (";" ~ slice)*)? ~ "]" } + +// Types + +ty = { + ty_bits + | ty_boolean +} + +ty_bits = { "Type_Bits(" ~ expr ~ ")" } +ty_boolean = { "Type_Constructor(\"boolean\")" } + +// Variables + +var = { + var_paren + | var_ident +} + +var_paren = _{ "(" ~ var ~ ")" } +var_ident = { ident } + +vars = { "[" ~ (var ~ (";" ~ var)*)? ~ "]" } + +// Identifiers + +func_ident = ${ "\"" ~ id ~ "." ~ decimal ~ "\"" } + +ident = @{ "\"" ~ id ~ "\"" } + +id = @{ + name ~ "." ~ name + | name +} + +name = @{ + (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* +} + +// Literals + +integer = @{ decimal } + +bits = @{ "\'" ~ binary ~ "\'" } + +decimal = @{ ASCII_DIGIT+ } + +binary = @{ ASCII_BIN_DIGIT+ } diff --git a/cranelift/isle/veri/aslp/src/ast.rs b/cranelift/isle/veri/aslp/src/ast.rs new file mode 100644 index 000000000000..eb3c1cb60d6d --- /dev/null +++ b/cranelift/isle/veri/aslp/src/ast.rs @@ -0,0 +1,96 @@ +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Block { + pub stmts: Vec, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Stmt { + ConstDecl { + ty: Type, + name: String, + rhs: Expr, + }, + VarDecl { + ty: Type, + name: String, + rhs: Expr, + }, + VarDeclsNoInit { + ty: Type, + names: Vec, + }, + Assign { + lhs: LExpr, + rhs: Expr, + }, + Assert { + cond: Expr, + }, + If { + cond: Expr, + then_block: Block, + else_block: Block, + }, + Call { + func: Func, + types: Vec, + args: Vec, + }, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum LExpr { + ArrayIndex { array: Box, index: Box }, + Field { x: Box, name: String }, + Var(String), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Expr { + Apply { + func: Func, + types: Vec, + args: Vec, + }, + ArrayIndex { + array: Box, + index: Box, + }, + Field { + x: Box, + name: String, + }, + Slices { + x: Box, + slices: Vec, + }, + Var(String), + LitInt(String), + LitBits(String), +} + +impl Expr { + pub fn as_lit_int(&self) -> Option<&String> { + match self { + Expr::LitInt(i) => Some(i), + _ => None, + } + } +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Slice { + LowWidth(Box, Box), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Func { + pub name: String, + pub id: usize, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Type { + Bits(Box), + Bool, +} diff --git a/cranelift/isle/veri/aslp/src/client.rs b/cranelift/isle/veri/aslp/src/client.rs new file mode 100644 index 000000000000..736ba2aa1e70 --- /dev/null +++ b/cranelift/isle/veri/aslp/src/client.rs @@ -0,0 +1,50 @@ +use anyhow::{bail, Result}; +use reqwest::IntoUrl; +use serde::Deserialize; +use tracing::debug; + +use crate::{ast::Block, opcode::Opcode, parser}; + +pub struct Client<'a> { + client: &'a reqwest::blocking::Client, + server_url: reqwest::Url, +} + +impl<'a> Client<'a> { + pub fn new(client: &'a reqwest::blocking::Client, server_url: U) -> Result { + Ok(Self { + client, + server_url: server_url.into_url()?, + }) + } + + pub fn opcode(&self, opcode: Opcode) -> Result { + // Model for response JSON data. + #[derive(Deserialize, Debug)] + struct Response { + instruction: String, + semantics: String, + } + + // Issue GET request. + let opcode = opcode.to_string(); + let res: Response = self + .client + .get(self.server_url.clone()) + .query(&[("opcode", &opcode)]) + .send()? + .json()?; + + debug!(%res.semantics); + + // Ensure response instruction matches. + if res.instruction != opcode { + bail!("response opcode mismatch"); + } + + // Parse semantics. + let block = parser::parse(&res.semantics)?; + + Ok(block) + } +} diff --git a/cranelift/isle/veri/aslp/src/lib.rs b/cranelift/isle/veri/aslp/src/lib.rs new file mode 100644 index 000000000000..6ce9c5b2c92a --- /dev/null +++ b/cranelift/isle/veri/aslp/src/lib.rs @@ -0,0 +1,4 @@ +pub mod ast; +pub mod client; +pub mod opcode; +pub mod parser; diff --git a/cranelift/isle/veri/aslp/src/main.rs b/cranelift/isle/veri/aslp/src/main.rs new file mode 100644 index 000000000000..a6f4be420d6f --- /dev/null +++ b/cranelift/isle/veri/aslp/src/main.rs @@ -0,0 +1,38 @@ +use anyhow::Result; +use clap::Parser as ClapParser; +use cranelift_isle_veri_aslp::parser; +use std::{fs, path::PathBuf}; + +#[derive(ClapParser)] +#[command(version, about)] +struct Args { + /// Input file to be formatted + file: PathBuf, + + /// Print debugging output (repeat for more detail) + #[arg(short = 'd', long = "debug", action = clap::ArgAction::Count)] + debug_level: u8, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + tracing_subscriber::fmt() + .with_timer(tracing_subscriber::fmt::time::uptime()) + .with_level(true) + .with_target(false) + .with_max_level(match args.debug_level { + 0 => tracing::Level::WARN, + 1 => tracing::Level::INFO, + 2 => tracing::Level::DEBUG, + _ => tracing::Level::TRACE, + }) + .init(); + + let src = fs::read_to_string(args.file).unwrap(); + + let block = parser::parse(&src)?; + println!("ast = {block:?}"); + + Ok(()) +} diff --git a/cranelift/isle/veri/aslp/src/opcode.rs b/cranelift/isle/veri/aslp/src/opcode.rs new file mode 100644 index 000000000000..245c29eddd85 --- /dev/null +++ b/cranelift/isle/veri/aslp/src/opcode.rs @@ -0,0 +1,55 @@ +pub struct Opcode { + pub segments: Vec, +} + +impl Opcode { + pub fn from_u32(x: u32) -> Self { + Opcode { + segments: vec![Segment::from_u32(x)], + } + } +} + +impl std::fmt::Display for Opcode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.segments + .iter() + .rev() + .map(|s| s.to_string()) + .collect::>() + .join("|") + ) + } +} + +pub enum Segment { + Symbolic(String, usize), + Constant(u32, usize), +} + +impl Segment { + pub fn from_u32(x: u32) -> Self { + Segment::Constant(x, 32) + } + + pub fn width(&self) -> usize { + match self { + Segment::Symbolic(_, w) | Segment::Constant(_, w) => *w, + } + } +} + +impl std::fmt::Display for Segment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Segment::Symbolic(s, w) => write!(f, "{s}:{w}"), + Segment::Constant(c, w) if w % 4 == 0 => { + write!(f, "0x{c:0>nibbles$x}", nibbles = w / 4) + } + Segment::Constant(c, w) => write!(f, "{c:#x}:{w}"), + } + } +} diff --git a/cranelift/isle/veri/aslp/src/parser.rs b/cranelift/isle/veri/aslp/src/parser.rs new file mode 100644 index 000000000000..cc82e7d8f3ad --- /dev/null +++ b/cranelift/isle/veri/aslp/src/parser.rs @@ -0,0 +1,276 @@ +use anyhow::Result; +use enquote::unquote; +use pest::{ + iterators::{Pair, Pairs}, + Parser, +}; +use pest_derive::Parser; +use tracing::debug; + +use crate::ast::{Block, Expr, Func, LExpr, Slice, Stmt, Type}; + +#[derive(Parser)] +#[grammar = "aslt.pest"] +struct ASLTParser; + +pub fn parse(src: &str) -> Result { + let pairs = ASLTParser::parse(Rule::aslt, src)?; + parse_block(pairs) +} + +fn parse_block(pairs: Pairs) -> Result { + let stmts = parse_stmts(pairs)?; + Ok(Block { stmts }) +} + +fn parse_stmts(pairs: Pairs) -> Result> { + let mut stmts = Vec::new(); + for pair in pairs { + let rule = pair.as_rule(); + debug!(?rule, "parse stmts"); + match rule { + Rule::stmt => stmts.push(parse_stmt(pair)?), + Rule::EOI => break, + _ => unreachable!("unexpected statement: {pair}"), + } + } + Ok(stmts) +} + +fn parse_stmt(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse stmt"); + match rule { + Rule::stmt => parse_stmt(pair.into_inner().next().unwrap()), + Rule::stmt_assign => { + let mut pairs = pair.into_inner(); + let lhs = parse_lexpr(pairs.next().unwrap())?; + let rhs = parse_expr(pairs.next().unwrap())?; + Ok(Stmt::Assign { lhs, rhs }) + } + Rule::stmt_constdecl => { + let mut pairs = pair.into_inner(); + let ty = parse_type(pairs.next().unwrap())?; + let name = parse_ident(pairs.next().unwrap())?; + let rhs = parse_expr(pairs.next().unwrap())?; + Ok(Stmt::ConstDecl { ty, name, rhs }) + } + Rule::stmt_vardecl => { + let mut pairs = pair.into_inner(); + let ty = parse_type(pairs.next().unwrap())?; + let name = parse_ident(pairs.next().unwrap())?; + let rhs = parse_expr(pairs.next().unwrap())?; + Ok(Stmt::VarDecl { ty, name, rhs }) + } + Rule::stmt_vardeclsnoinit => { + let mut pairs = pair.into_inner(); + let ty = parse_type(pairs.next().unwrap())?; + let names = parse_vars(pairs.next().unwrap().into_inner())?; + Ok(Stmt::VarDeclsNoInit { ty, names }) + } + Rule::stmt_assert => { + let cond = parse_expr(pair.into_inner().next().unwrap())?; + Ok(Stmt::Assert { cond }) + } + Rule::stmt_if => { + let mut pairs = pair.into_inner(); + let cond = parse_expr(pairs.next().unwrap())?; + let then_block = parse_block(pairs.next().unwrap().into_inner())?; + let elseif_block = parse_block(pairs.next().unwrap().into_inner())?; + if !elseif_block.stmts.is_empty() { + todo!("else if"); + } + let else_block = parse_block(pairs.next().unwrap().into_inner())?; + Ok(Stmt::If { + cond, + then_block, + else_block, + }) + } + Rule::stmt_tcall => { + let mut pairs = pair.into_inner(); + let func = parse_func_ident(pairs.next().unwrap())?; + let types = parse_exprs(pairs.next().unwrap().into_inner())?; + let args = parse_exprs(pairs.next().unwrap().into_inner())?; + Ok(Stmt::Call { func, types, args }) + } + _ => unreachable!("unexpected statement: {rule:?}"), + } +} + +fn parse_lexpr(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse lexpr"); + match rule { + Rule::lexpr => parse_lexpr(pair.into_inner().next().unwrap()), + Rule::lexpr_array => { + let mut pairs = pair.into_inner(); + let array = Box::new(parse_lexpr(pairs.next().unwrap())?); + let index = Box::new(parse_expr(pairs.next().unwrap())?); + Ok(LExpr::ArrayIndex { array, index }) + } + Rule::lexpr_field => { + let mut pairs = pair.into_inner(); + let x = Box::new(parse_lexpr(pairs.next().unwrap())?); + let name = parse_ident(pairs.next().unwrap())?; + Ok(LExpr::Field { x, name }) + } + Rule::lexpr_var => { + let var = parse_var(pair.into_inner().next().unwrap())?; + Ok(LExpr::Var(var)) + } + _ => unreachable!("unexpected lexpr: {rule:?}"), + } +} + +fn parse_expr(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse expr"); + match rule { + Rule::expr => parse_expr(pair.into_inner().next().unwrap()), + Rule::expr_array => { + let mut pairs = pair.into_inner(); + let array = Box::new(parse_expr(pairs.next().unwrap())?); + let index = Box::new(parse_expr(pairs.next().unwrap())?); + Ok(Expr::ArrayIndex { array, index }) + } + Rule::expr_tapply => { + let mut pairs = pair.into_inner(); + let func = parse_func_ident(pairs.next().unwrap())?; + let types = parse_exprs(pairs.next().unwrap().into_inner())?; + let args = parse_exprs(pairs.next().unwrap().into_inner())?; + Ok(Expr::Apply { func, types, args }) + } + Rule::expr_slices => { + let mut pairs = pair.into_inner(); + let x = Box::new(parse_expr(pairs.next().unwrap())?); + let slices = parse_slices(pairs.next().unwrap().into_inner())?; + Ok(Expr::Slices { x, slices }) + } + Rule::expr_field => { + let mut pairs = pair.into_inner(); + let x = Box::new(parse_expr(pairs.next().unwrap())?); + let name = parse_ident(pairs.next().unwrap())?; + Ok(Expr::Field { x, name }) + } + Rule::expr_var => { + let var = parse_var(pair.into_inner().next().unwrap())?; + Ok(Expr::Var(var)) + } + Rule::expr_litint => { + let digits = parse_literal(pair.into_inner().next().unwrap())?; + Ok(Expr::LitInt(digits)) + } + Rule::expr_litbits => { + let bits = parse_literal(pair.into_inner().next().unwrap())?; + Ok(Expr::LitBits(bits)) + } + _ => unreachable!("unexpected expr: {rule:?}"), + } +} + +fn parse_exprs(pairs: Pairs) -> Result> { + let mut exprs = Vec::new(); + for pair in pairs { + let rule = pair.as_rule(); + debug!(?rule, "parse exprs"); + match rule { + Rule::expr => exprs.push(parse_expr(pair)?), + _ => unreachable!("unexpected expression: {rule:?}"), + } + } + Ok(exprs) +} + +fn parse_slice(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse slice"); + match rule { + Rule::slice => parse_slice(pair.into_inner().next().unwrap()), + Rule::slice_lowd => { + let mut pairs = pair.into_inner(); + let low = Box::new(parse_expr(pairs.next().unwrap())?); + let width = Box::new(parse_expr(pairs.next().unwrap())?); + Ok(Slice::LowWidth(low, width)) + } + _ => unreachable!("unexpected slice: {rule:?}"), + } +} + +fn parse_slices(pairs: Pairs) -> Result> { + let mut slices = Vec::new(); + for pair in pairs { + let rule = pair.as_rule(); + debug!(?rule, "parse slices"); + match rule { + Rule::slice => slices.push(parse_slice(pair)?), + _ => unreachable!("unexpected slice: {rule:?}"), + } + } + Ok(slices) +} + +fn parse_type(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse type"); + match rule { + Rule::ty => parse_type(pair.into_inner().next().unwrap()), + Rule::ty_bits => { + let width = Box::new(parse_expr(pair.into_inner().next().unwrap())?); + Ok(Type::Bits(width)) + } + Rule::ty_boolean => Ok(Type::Bool), + _ => unreachable!("unexpected type: {rule:?}"), + } +} + +fn parse_var(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse var"); + match rule { + Rule::var => parse_var(pair.into_inner().next().unwrap()), + Rule::var_ident => parse_ident(pair), + _ => unreachable!("unexpected var: {rule:?}"), + } +} + +fn parse_vars(pairs: Pairs) -> Result> { + let mut vars = Vec::new(); + for pair in pairs { + let rule = pair.as_rule(); + debug!(?rule, "parse vars"); + match rule { + Rule::var => vars.push(parse_var(pair)?), + _ => unreachable!("unexpected var: {rule:?}"), + } + } + Ok(vars) +} + +fn parse_func_ident(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse func ident"); + match rule { + Rule::func_ident => { + let mut pairs = pair.into_inner(); + let name = pairs.next().unwrap().as_str().to_string(); + let id = pairs.next().unwrap().as_str().parse()?; + Ok(Func { name, id }) + } + _ => unreachable!("unexpected func ident: {rule:?}"), + } +} + +fn parse_ident(pair: Pair) -> Result { + Ok(unquote(pair.as_str())?) +} + +fn parse_literal(pair: Pair) -> Result { + let rule = pair.as_rule(); + debug!(?rule, "parse literal"); + match rule { + Rule::integer => Ok(pair.as_str().to_string()), + Rule::bits => Ok(unquote(pair.as_str())?), + _ => unreachable!("unexpected literal: {rule:?}"), + } +} diff --git a/cranelift/isle/veri/aslp/tests/data/add.aslt b/cranelift/isle/veri/aslp/tests/data/add.aslt new file mode 100644 index 000000000000..61d51785c6dc --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/add.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),4),Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),5);Expr_Array(Expr_Var("_R"),6)])) diff --git a/cranelift/isle/veri/aslp/tests/data/add32.aslt b/cranelift/isle/veri/aslp/tests/data/add32.aslt new file mode 100644 index 000000000000..e416b4cdef7c --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/add32.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),4),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("add_bits.0",[32],[Expr_Slices(Expr_Array(Expr_Var("_R"),5),[Slice_LoWd(0,32)]);Expr_Slices(Expr_Array(Expr_Var("_R"),6),[Slice_LoWd(0,32)])]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/add_extend.aslt b/cranelift/isle/veri/aslp/tests/data/add_extend.aslt new file mode 100644 index 000000000000..79f9e0a2148a --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/add_extend.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),15),Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),16);Expr_Array(Expr_Var("_R"),17)])) diff --git a/cranelift/isle/veri/aslp/tests/data/add_imm.aslt b/cranelift/isle/veri/aslp/tests/data/add_imm.aslt new file mode 100644 index 000000000000..b3e66dd32905 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/add_imm.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),7),Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),8);'0000000000000000000000000000000000000000000000000000000100100011'])) diff --git a/cranelift/isle/veri/aslp/tests/data/add_shift.aslt b/cranelift/isle/veri/aslp/tests/data/add_shift.aslt new file mode 100644 index 000000000000..75594510902d --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/add_shift.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("append_bits.0",[60;4],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,60)]);'0000'])])) diff --git a/cranelift/isle/veri/aslp/tests/data/addc.aslt b/cranelift/isle/veri/aslp/tests/data/addc.aslt new file mode 100644 index 000000000000..abc0285a79a6 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/addc.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),4),Expr_TApply("add_bits.0",[64],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),5);Expr_Array(Expr_Var("_R"),6)]);Expr_TApply("ZeroExtend.0",[1;64],[Expr_Slices(Expr_Field(Expr_Var("PSTATE"),"C"),[Slice_LoWd(0,1)]);64])])) diff --git a/cranelift/isle/veri/aslp/tests/data/adds.aslt b/cranelift/isle/veri/aslp/tests/data/adds.aslt new file mode 100644 index 000000000000..ba28761581df --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/adds.aslt @@ -0,0 +1,6 @@ +Stmt_ConstDecl(Type_Bits(64),"Cse0__5",Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_Array(Expr_Var("_R"),3)])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"V"),Expr_TApply("not_bits.0",[1],[Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[128],[Expr_TApply("SignExtend.0",[64;128],[Expr_Var("Cse0__5");128]);Expr_TApply("add_bits.0",[128],[Expr_TApply("SignExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),2);128]);Expr_TApply("SignExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),3);128])])])])])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"C"),Expr_TApply("not_bits.0",[1],[Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[128],[Expr_TApply("ZeroExtend.0",[64;128],[Expr_Var("Cse0__5");128]);Expr_TApply("add_bits.0",[128],[Expr_TApply("ZeroExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),2);128]);Expr_TApply("ZeroExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),3);128])])])])])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"Z"),Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000000'])])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"N"),Expr_Slices(Expr_Var("Cse0__5"),[Slice_LoWd(63,1)])) +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_Var("Cse0__5")) diff --git a/cranelift/isle/veri/aslp/tests/data/ccmp.aslt b/cranelift/isle/veri/aslp/tests/data/ccmp.aslt new file mode 100644 index 000000000000..658d6d299eaf --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ccmp.aslt @@ -0,0 +1,13 @@ +Stmt_ConstDecl(Type_Bits(64),"Cse2__5",Expr_TApply("not_bits.0",[64],[Expr_Array(Expr_Var("_R"),1)])) +Stmt_ConstDecl(Type_Bits(64),"Cse0__5",Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),22);Expr_TApply("not_bits.0",[64],[Expr_Array(Expr_Var("_R"),1)])])) +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Field(Expr_Var("PSTATE"),"Z");'1']),[ +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"V"),Expr_TApply("not_bits.0",[1],[Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[128],[Expr_TApply("SignExtend.0",[64;128],[Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']);128]);Expr_TApply("add_bits.0",[128],[Expr_TApply("add_bits.0",[128],[Expr_TApply("SignExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),22);128]);Expr_TApply("SignExtend.0",[64;128],[Expr_Var("Cse2__5");128])]);'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'])])])])); +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"C"),Expr_TApply("not_bits.0",[1],[Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[128],[Expr_TApply("ZeroExtend.0",[64;128],[Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']);128]);Expr_TApply("add_bits.0",[128],[Expr_TApply("add_bits.0",[128],[Expr_TApply("ZeroExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),22);128]);Expr_TApply("ZeroExtend.0",[64;128],[Expr_Var("Cse2__5");128])]);'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'])])])])); +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"Z"),Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[64],[Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']);'0000000000000000000000000000000000000000000000000000000000000000'])])); +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"N"),Expr_Slices(Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']),[Slice_LoWd(63,1)])) +],[],[ +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"V"),'1'); +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"C"),'0'); +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"Z"),'1'); +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"N"),'1') +]) diff --git a/cranelift/isle/veri/aslp/tests/data/clz.aslt b/cranelift/isle/veri/aslp/tests/data/clz.aslt new file mode 100644 index 000000000000..31be61b281e7 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/clz.aslt @@ -0,0 +1,259 @@ +Stmt_VarDeclsNoInit(Type_Bits(16),["HighestSetBit6__6"]) +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(63,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(62,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(61,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(60,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(59,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(58,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(57,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(56,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000111000') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(55,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(54,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(53,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(52,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(51,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(50,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(49,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(48,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000110000') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(47,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(46,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(45,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(44,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(43,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(42,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(41,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(40,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000101000') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(39,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(38,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(37,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(36,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(35,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(34,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(33,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(32,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000100000') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(31,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(30,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(29,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(28,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(27,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(26,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(25,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(24,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000011000') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(23,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(22,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(21,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(20,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(19,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(18,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(17,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(16,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000010000') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(15,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(14,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(13,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(12,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(11,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(10,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(9,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(8,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000001000') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(7,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000111') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(6,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000110') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(5,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000101') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(4,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000100') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(3,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000011') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(2,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000010') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(1,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000001') +],[],[ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,1)]);'1']),[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'0000000000000000') +],[],[ +Stmt_Assign(LExpr_Var("HighestSetBit6__6"),'1111111111111111') +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +]) +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),15),Expr_TApply("SignExtend.0",[16;64],[Expr_TApply("sub_bits.0",[16],['0000000001000000';Expr_TApply("add_bits.0",[16],[Expr_Var("HighestSetBit6__6");'0000000000000001'])]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/csel.aslt b/cranelift/isle/veri/aslp/tests/data/csel.aslt new file mode 100644 index 000000000000..4932d0d967cc --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/csel.aslt @@ -0,0 +1,5 @@ +Stmt_If(Expr_TApply("eq_bits.0",[1],[Expr_Field(Expr_Var("PSTATE"),"C");'1']),[ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),10),Expr_Array(Expr_Var("_R"),12)) +],[],[ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),10),Expr_Array(Expr_Var("_R"),14)) +]) diff --git a/cranelift/isle/veri/aslp/tests/data/generate.sh b/cranelift/isle/veri/aslp/tests/data/generate.sh new file mode 100755 index 000000000000..436b3d08da3e --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/generate.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +# Assemble AArch64 assembly to 32-bit hex opcode. +function assemble() { + local asm="$1" + + echo "${asm}" \ + | clang -x assembler --target=aarch64 -march=armv8-a+sha2 - -c -o /dev/stdout \ + | llvm-objdump - -d --section=.text \ + | tail -n1 \ + | awk '/0:/ { print $2 }' +} + +# Generate semantics for opcode in ASLT form. +function aslt() { + local opcode="$1" + + { + echo ':set impdef "Has SHA1 Crypto instructions" = TRUE' + echo ":ast A64 0x${opcode}" + } | asli +} + +# Generate named testcase for a given assembly instruction. +function testcase() { + local name="$1" + local asm="$2" + + opcode=$(assemble "${asm}") + aslt "${opcode}" > "${name}.aslt" +} + +# Testcases. +# Arithmetic. +testcase "add" "add x4, x5, x6" +testcase "add32" "add w4, w5, w6" +testcase "add_imm" "add x7, x8, #291" +testcase "add_shift" "add x1, x2, x3, lsl 4" +testcase "add_extend" "add x15, x16, x17, uxtx" +testcase "addc" "adc x4, x5, x6" +testcase "adds" "adds x1, x2, x3" +testcase "subs" "subs x10, x11, x12, lsl 23" +testcase "subsp" "sub sp, sp, #32" + +# 8-bit loads. +testcase "ldrb" "ldrb w1, [x2]" +testcase "ldrb_reg_reg" "ldrb w1, [x2, x3]" +testcase "ldrb_reg_scale" "ldrb w1, [x2, x3, lsl #0]" +testcase "ldrb_reg_scale_ext_uxtw" "ldrb w1, [x2, w3, uxtw #0]" +testcase "ldrb_reg_scale_ext_sxtw" "ldrb w1, [x2, w3, sxtw #0]" +testcase "ldrb_reg_scale_ext_sxtx" "ldrb w1, [x2, x3, sxtx #0]" + +# 16-bit loads. +testcase "ldrh" "ldrh w1, [x2]" +testcase "ldrh_reg_reg" "ldrh w1, [x2, x3]" +testcase "ldrh_reg_scale" "ldrh w1, [x2, x3, lsl #0]" +testcase "ldrh_reg_scale1" "ldrh w1, [x2, x3, lsl #1]" +testcase "ldrh_reg_scale_ext_uxtw" "ldrh w1, [x2, w3, uxtw #0]" +testcase "ldrh_reg_scale_ext_sxtw" "ldrh w1, [x2, w3, sxtw #0]" +testcase "ldrh_reg_scale_ext_sxtx" "ldrh w1, [x2, x3, sxtx #0]" + +# 32-bit loads. +testcase "ldrsw_reg_reg" "ldrsw x1, [x2, x3]" +testcase "ldrsw_reg_scale" "ldrsw x1, [x2, x3, lsl #0]" +testcase "ldrsw_reg_scale2" "ldrsw x1, [x2, x3, lsl #2]" +testcase "ldrsw_reg_scale_ext_uxtw" "ldrsw x1, [x2, w3, uxtw #0]" +testcase "ldrsw_reg_scale_ext_sxtw" "ldrsw x1, [x2, w3, sxtw #0]" +testcase "ldrsw_reg_scale_ext_sxtx" "ldrsw x1, [x2, x3, sxtx #0]" + +# Misc. +testcase "csel" "csel x10, x12, x14, hs" +testcase "ccmp" "ccmp x22, x1, 13, eq" +testcase "clz" "clz x15, x3" +testcase "ldp" "ldp x1, x2, [x3], #128" +testcase "stp" "stp x1, x2, [x3], #128" +testcase "ucvtf" "ucvtf d0, w2" +testcase "sha1h" "sha1h s17, s6" +testcase "sha1su1" "sha1su1 v2.4s, v1.4s" +testcase "sha1su0" "sha1su0 v3.4s, v0.4s, v1.4s" +testcase "mrs" "mrs x0, nzcv" +testcase "tbl" "tbl v0.8b, {v0.16b}, v0.8b" +testcase "uqsub" "uqsub v3.4s, v1.4s, v2.4s" diff --git a/cranelift/isle/veri/aslp/tests/data/ldp.aslt b/cranelift/isle/veri/aslp/tests/data/ldp.aslt new file mode 100644 index 000000000000..1f30e7d28c30 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldp.aslt @@ -0,0 +1,3 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("Mem.read.0",[8],[Expr_Array(Expr_Var("_R"),3);8;0])) +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),2),Expr_TApply("Mem.read.0",[8],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),3);'0000000000000000000000000000000000000000000000000000000000001000']);8;0])) +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),3),Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),3);'0000000000000000000000000000000000000000000000000000000010000000'])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrb.aslt b/cranelift/isle/veri/aslp/tests/data/ldrb.aslt new file mode 100644 index 000000000000..dbf1921593e6 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrb.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[8;32],[Expr_TApply("Mem.read.0",[1],[Expr_Array(Expr_Var("_R"),2);1;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrb_reg_reg.aslt b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_reg.aslt new file mode 100644 index 000000000000..d5cc003630ec --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_reg.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[8;32],[Expr_TApply("Mem.read.0",[1],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_Array(Expr_Var("_R"),3)]);1;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale.aslt b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale.aslt new file mode 100644 index 000000000000..d5cc003630ec --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[8;32],[Expr_TApply("Mem.read.0",[1],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_Array(Expr_Var("_R"),3)]);1;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_sxtw.aslt b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_sxtw.aslt new file mode 100644 index 000000000000..c3ec225c8e4f --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_sxtw.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[8;32],[Expr_TApply("Mem.read.0",[1],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("SignExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,32)]);64])]);1;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_sxtx.aslt b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_sxtx.aslt new file mode 100644 index 000000000000..8d603874cf7a --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_sxtx.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[8;32],[Expr_TApply("Mem.read.0",[1],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("SignExtend.0",[64;64],[Expr_Array(Expr_Var("_R"),3);64])]);1;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_uxtw.aslt b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_uxtw.aslt new file mode 100644 index 000000000000..f6e9c46e835c --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrb_reg_scale_ext_uxtw.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[8;32],[Expr_TApply("Mem.read.0",[1],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,32)]);64])]);1;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrh.aslt b/cranelift/isle/veri/aslp/tests/data/ldrh.aslt new file mode 100644 index 000000000000..d412023d70d7 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrh.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("Mem.read.0",[2],[Expr_Array(Expr_Var("_R"),2);2;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrh_reg_reg.aslt b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_reg.aslt new file mode 100644 index 000000000000..378cdd47795a --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_reg.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("Mem.read.0",[2],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_Array(Expr_Var("_R"),3)]);2;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale.aslt b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale.aslt new file mode 100644 index 000000000000..378cdd47795a --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("Mem.read.0",[2],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_Array(Expr_Var("_R"),3)]);2;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale1.aslt b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale1.aslt new file mode 100644 index 000000000000..951e3a8e4d55 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale1.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("Mem.read.0",[2],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("append_bits.0",[63;1],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,63)]);'0'])]);2;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_sxtw.aslt b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_sxtw.aslt new file mode 100644 index 000000000000..fdcbc74744ca --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_sxtw.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("Mem.read.0",[2],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("SignExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,32)]);64])]);2;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_sxtx.aslt b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_sxtx.aslt new file mode 100644 index 000000000000..60e1fb3dc348 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_sxtx.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("Mem.read.0",[2],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("SignExtend.0",[64;64],[Expr_Array(Expr_Var("_R"),3);64])]);2;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_uxtw.aslt b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_uxtw.aslt new file mode 100644 index 000000000000..61fe3bdf96c8 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrh_reg_scale_ext_uxtw.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("ZeroExtend.0",[32;64],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("Mem.read.0",[2],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,32)]);64])]);2;0]);32]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_reg.aslt b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_reg.aslt new file mode 100644 index 000000000000..a45921964412 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_reg.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("SignExtend.0",[32;64],[Expr_TApply("Mem.read.0",[4],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_Array(Expr_Var("_R"),3)]);4;0]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale.aslt b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale.aslt new file mode 100644 index 000000000000..a45921964412 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("SignExtend.0",[32;64],[Expr_TApply("Mem.read.0",[4],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_Array(Expr_Var("_R"),3)]);4;0]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale2.aslt b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale2.aslt new file mode 100644 index 000000000000..000a5dc8fd61 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale2.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("SignExtend.0",[32;64],[Expr_TApply("Mem.read.0",[4],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("append_bits.0",[62;2],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,62)]);'00'])]);4;0]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_sxtw.aslt b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_sxtw.aslt new file mode 100644 index 000000000000..819d476cfc46 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_sxtw.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("SignExtend.0",[32;64],[Expr_TApply("Mem.read.0",[4],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("SignExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,32)]);64])]);4;0]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_sxtx.aslt b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_sxtx.aslt new file mode 100644 index 000000000000..5177f8418ab2 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_sxtx.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("SignExtend.0",[32;64],[Expr_TApply("Mem.read.0",[4],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("SignExtend.0",[64;64],[Expr_Array(Expr_Var("_R"),3);64])]);4;0]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_uxtw.aslt b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_uxtw.aslt new file mode 100644 index 000000000000..a3ef5e0d1f53 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ldrsw_reg_scale_ext_uxtw.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),1),Expr_TApply("SignExtend.0",[32;64],[Expr_TApply("Mem.read.0",[4],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),2);Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_R"),3),[Slice_LoWd(0,32)]);64])]);4;0]);64])) diff --git a/cranelift/isle/veri/aslp/tests/data/mrs.aslt b/cranelift/isle/veri/aslp/tests/data/mrs.aslt new file mode 100644 index 000000000000..2ad746f40ac2 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/mrs.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),0),Expr_TApply("append_bits.0",[36;28],[Expr_TApply("ZeroExtend.0",[4;36],[Expr_TApply("append_bits.0",[3;1],[Expr_TApply("append_bits.0",[2;1],[Expr_TApply("append_bits.0",[1;1],[Expr_Field(Expr_Var("PSTATE"),"N");Expr_Field(Expr_Var("PSTATE"),"Z")]);Expr_Field(Expr_Var("PSTATE"),"C")]);Expr_Field(Expr_Var("PSTATE"),"V")]);36]);'0000000000000000000000000000'])) diff --git a/cranelift/isle/veri/aslp/tests/data/sha1h.aslt b/cranelift/isle/veri/aslp/tests/data/sha1h.aslt new file mode 100644 index 000000000000..f96ab7260799 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/sha1h.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_Z"),17),Expr_TApply("ZeroExtend.0",[32;128],[Expr_TApply("append_bits.0",[2;30],[Expr_Slices(Expr_Array(Expr_Var("_Z"),6),[Slice_LoWd(0,2)]);Expr_Slices(Expr_Array(Expr_Var("_Z"),6),[Slice_LoWd(2,30)])]);128])) diff --git a/cranelift/isle/veri/aslp/tests/data/sha1su0.aslt b/cranelift/isle/veri/aslp/tests/data/sha1su0.aslt new file mode 100644 index 000000000000..9bdfbbe89a1a --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/sha1su0.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Array(LExpr_Var("_Z"),3),Expr_TApply("eor_bits.0",[128],[Expr_TApply("eor_bits.0",[128],[Expr_TApply("append_bits.0",[64;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(0,64)]);Expr_Slices(Expr_Array(Expr_Var("_Z"),3),[Slice_LoWd(64,64)])]);Expr_Array(Expr_Var("_Z"),3)]);Expr_Array(Expr_Var("_Z"),1)])) diff --git a/cranelift/isle/veri/aslp/tests/data/sha1su1.aslt b/cranelift/isle/veri/aslp/tests/data/sha1su1.aslt new file mode 100644 index 000000000000..c6668056b666 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/sha1su1.aslt @@ -0,0 +1,2 @@ +Stmt_ConstDecl(Type_Bits(128),"Cse0__5",Expr_TApply("ZeroExtend.0",[96;128],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(32,96)]);128])) +Stmt_Assign(LExpr_Array(LExpr_Var("_Z"),2),Expr_TApply("append_bits.0",[32;96],[Expr_TApply("eor_bits.0",[32],[Expr_TApply("append_bits.0",[31;1],[Expr_Slices(Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,127)]),[Slice_LoWd(96,31)]);Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(127,1)])]);Expr_TApply("append_bits.0",[30;2],[Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,30)]);Expr_Slices(Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,32)]),[Slice_LoWd(30,2)])])]);Expr_TApply("append_bits.0",[32;64],[Expr_TApply("append_bits.0",[31;1],[Expr_Slices(Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,95)]),[Slice_LoWd(64,31)]);Expr_Slices(Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,96)]),[Slice_LoWd(95,1)])]);Expr_TApply("append_bits.0",[32;32],[Expr_TApply("append_bits.0",[31;1],[Expr_Slices(Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,63)]),[Slice_LoWd(32,31)]);Expr_Slices(Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,64)]),[Slice_LoWd(63,1)])]);Expr_TApply("append_bits.0",[31;1],[Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,31)]);Expr_Slices(Expr_Slices(Expr_TApply("eor_bits.0",[128],[Expr_Array(Expr_Var("_Z"),2);Expr_Var("Cse0__5")]),[Slice_LoWd(0,32)]),[Slice_LoWd(31,1)])])])])])) diff --git a/cranelift/isle/veri/aslp/tests/data/stp.aslt b/cranelift/isle/veri/aslp/tests/data/stp.aslt new file mode 100644 index 000000000000..4951a9231acb --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/stp.aslt @@ -0,0 +1,3 @@ +Stmt_TCall("Mem.set.0",[8],[Expr_Array(Expr_Var("_R"),3);8;0;Expr_Array(Expr_Var("_R"),1)]) +Stmt_TCall("Mem.set.0",[8],[Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),3);'0000000000000000000000000000000000000000000000000000000000001000']);8;0;Expr_Array(Expr_Var("_R"),2)]) +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),3),Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),3);'0000000000000000000000000000000000000000000000000000000010000000'])) diff --git a/cranelift/isle/veri/aslp/tests/data/subs.aslt b/cranelift/isle/veri/aslp/tests/data/subs.aslt new file mode 100644 index 000000000000..e95f85865a3d --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/subs.aslt @@ -0,0 +1,7 @@ +Stmt_ConstDecl(Type_Bits(64),"Cse2__5",Expr_TApply("append_bits.0",[41;23],[Expr_Slices(Expr_Array(Expr_Var("_R"),12),[Slice_LoWd(0,41)]);'00000000000000000000000'])) +Stmt_ConstDecl(Type_Bits(64),"Cse0__5",Expr_TApply("add_bits.0",[64],[Expr_Array(Expr_Var("_R"),11);Expr_TApply("not_bits.0",[64],[Expr_TApply("append_bits.0",[41;23],[Expr_Slices(Expr_Array(Expr_Var("_R"),12),[Slice_LoWd(0,41)]);'00000000000000000000000'])])])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"V"),Expr_TApply("not_bits.0",[1],[Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[128],[Expr_TApply("SignExtend.0",[64;128],[Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']);128]);Expr_TApply("add_bits.0",[128],[Expr_TApply("add_bits.0",[128],[Expr_TApply("SignExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),11);128]);Expr_TApply("SignExtend.0",[64;128],[Expr_TApply("not_bits.0",[64],[Expr_Var("Cse2__5")]);128])]);'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'])])])])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"C"),Expr_TApply("not_bits.0",[1],[Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[128],[Expr_TApply("ZeroExtend.0",[64;128],[Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']);128]);Expr_TApply("add_bits.0",[128],[Expr_TApply("add_bits.0",[128],[Expr_TApply("ZeroExtend.0",[64;128],[Expr_Array(Expr_Var("_R"),11);128]);Expr_TApply("ZeroExtend.0",[64;128],[Expr_TApply("not_bits.0",[64],[Expr_Var("Cse2__5")]);128])]);'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'])])])])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"Z"),Expr_TApply("cvt_bool_bv.0",[],[Expr_TApply("eq_bits.0",[64],[Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']);'0000000000000000000000000000000000000000000000000000000000000000'])])) +Stmt_Assign(LExpr_Field(LExpr_Var("PSTATE"),"N"),Expr_Slices(Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001']),[Slice_LoWd(63,1)])) +Stmt_Assign(LExpr_Array(LExpr_Var("_R"),10),Expr_TApply("add_bits.0",[64],[Expr_Var("Cse0__5");'0000000000000000000000000000000000000000000000000000000000000001'])) diff --git a/cranelift/isle/veri/aslp/tests/data/subsp.aslt b/cranelift/isle/veri/aslp/tests/data/subsp.aslt new file mode 100644 index 000000000000..90de7e729a9d --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/subsp.aslt @@ -0,0 +1 @@ +Stmt_Assign(LExpr_Var("SP_EL0"),Expr_TApply("add_bits.0",[64],[Expr_Var("SP_EL0");'1111111111111111111111111111111111111111111111111111111111100000'])) diff --git a/cranelift/isle/veri/aslp/tests/data/tbl.aslt b/cranelift/isle/veri/aslp/tests/data/tbl.aslt new file mode 100644 index 000000000000..0f8d27548a0c --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/tbl.aslt @@ -0,0 +1,51 @@ +Stmt_ConstDecl(Type_Bits(16),"Cse15__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(0,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse14__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(0,8)]);16]);'0000000000001000'])) +Stmt_ConstDecl(Type_Bits(16),"Cse13__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(8,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse12__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(8,8)]);16]);'0000000000001000'])) +Stmt_ConstDecl(Type_Bits(16),"Cse11__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(16,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse10__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(16,8)]);16]);'0000000000001000'])) +Stmt_ConstDecl(Type_Bits(16),"Cse9__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(24,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse8__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(24,8)]);16]);'0000000000001000'])) +Stmt_ConstDecl(Type_Bits(16),"Cse7__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(32,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse6__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(32,8)]);16]);'0000000000001000'])) +Stmt_ConstDecl(Type_Bits(16),"Cse5__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(40,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse4__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(40,8)]);16]);'0000000000001000'])) +Stmt_ConstDecl(Type_Bits(16),"Cse3__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(48,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse2__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(48,8)]);16]);'0000000000001000'])) +Stmt_ConstDecl(Type_Bits(16),"Cse1__5",Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(56,8)]);16])) +Stmt_ConstDecl(Type_Bits(16),"Cse0__5",Expr_TApply("mul_bits.0",[16],[Expr_TApply("ZeroExtend.0",[8;16],[Expr_Slices(Expr_Array(Expr_Var("_Z"),0),[Slice_LoWd(56,8)]);16]);'0000000000001000'])) +Stmt_VarDeclsNoInit(Type_Bits(64),["result__4"]) +Stmt_Assign(LExpr_Var("result__4"),'0000000000000000000000000000000000000000000000000000000000000000') +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse15__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse14__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("ZeroExtend.0",[8;64],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse14__5");32])]),[Slice_LoWd(0,8)]);64])) +],[],[]) +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse13__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse12__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("append_bits.0",[48;16],[Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(16,48)]);Expr_TApply("append_bits.0",[8;8],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse12__5");32])]),[Slice_LoWd(0,8)]);Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(0,8)])])])) +],[],[]) +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse11__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse10__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("append_bits.0",[40;24],[Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(24,40)]);Expr_TApply("append_bits.0",[8;16],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse10__5");32])]),[Slice_LoWd(0,8)]);Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(0,16)])])])) +],[],[]) +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse9__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse8__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("append_bits.0",[32;32],[Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(32,32)]);Expr_TApply("append_bits.0",[8;24],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse8__5");32])]),[Slice_LoWd(0,8)]);Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(0,24)])])])) +],[],[]) +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse7__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse6__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("append_bits.0",[24;40],[Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(40,24)]);Expr_TApply("append_bits.0",[8;32],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse6__5");32])]),[Slice_LoWd(0,8)]);Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(0,32)])])])) +],[],[]) +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse5__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse4__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("append_bits.0",[16;48],[Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(48,16)]);Expr_TApply("append_bits.0",[8;40],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse4__5");32])]),[Slice_LoWd(0,8)]);Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(0,40)])])])) +],[],[]) +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse3__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse2__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("append_bits.0",[8;56],[Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(56,8)]);Expr_TApply("append_bits.0",[8;48],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse2__5");32])]),[Slice_LoWd(0,8)]);Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(0,48)])])])) +],[],[]) +Stmt_If(Expr_TApply("slt_bits.0",[16],[Expr_Var("Cse1__5");'0000000000010000']),[ +Stmt_Assert(Expr_TApply("and_bool.0",[],[Expr_Var("TRUE");Expr_TApply("sle_bits.0",[32],[Expr_TApply("ZeroExtend.0",[16;32],[Expr_TApply("add_bits.0",[16],[Expr_Var("Cse0__5");'0000000000001000']);32]);'00000000000000000000000010000000'])])); +Stmt_Assign(LExpr_Var("result__4"),Expr_TApply("append_bits.0",[8;56],[Expr_Slices(Expr_TApply("lsr_bits.0",[128;32],[Expr_Array(Expr_Var("_Z"),0);Expr_TApply("ZeroExtend.0",[16;32],[Expr_Var("Cse0__5");32])]),[Slice_LoWd(0,8)]);Expr_Slices(Expr_Var("result__4"),[Slice_LoWd(0,56)])])) +],[],[]) +Stmt_Assign(LExpr_Array(LExpr_Var("_Z"),0),Expr_TApply("ZeroExtend.0",[64;128],[Expr_Var("result__4");128])) diff --git a/cranelift/isle/veri/aslp/tests/data/ucvtf.aslt b/cranelift/isle/veri/aslp/tests/data/ucvtf.aslt new file mode 100644 index 000000000000..bb89eb8faa8c --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/ucvtf.aslt @@ -0,0 +1,4 @@ +Stmt_VarDeclsNoInit(Type_Bits(4),["FPDecodeRounding5__5"]) +Stmt_Assign(LExpr_Var("FPDecodeRounding5__5"),Expr_TApply("ZeroExtend.0",[2;4],[Expr_Slices(Expr_Var("FPCR"),[Slice_LoWd(22,2)]);4])) +Stmt_ConstDecl(Type_Bits(64),"Exp9__5",Expr_TApply("FixedToFP.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_R"),2),[Slice_LoWd(0,32)]);0;Expr_Var("TRUE");Expr_Var("FPCR");Expr_TApply("cvt_bits_uint.0",[4],[Expr_Var("FPDecodeRounding5__5")])])) +Stmt_Assign(LExpr_Array(LExpr_Var("_Z"),0),Expr_TApply("ZeroExtend.0",[64;128],[Expr_Var("Exp9__5");128])) diff --git a/cranelift/isle/veri/aslp/tests/data/uqsub.aslt b/cranelift/isle/veri/aslp/tests/data/uqsub.aslt new file mode 100644 index 000000000000..1a12a3cccc6c --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/data/uqsub.aslt @@ -0,0 +1,49 @@ +Stmt_VarDeclsNoInit(Type_Bits(32),["UnsignedSatQ18__6"]) +Stmt_VarDeclsNoInit(Type_Constructor("boolean"),["UnsignedSatQ19__6"]) +Stmt_If(Expr_TApply("slt_bits.0",[64],[Expr_TApply("sub_bits.0",[64],[Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(0,32)]);64]);Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(0,32)]);64])]);'0000000000000000000000000000000000000000000000000000000000000000']),[ +Stmt_Assign(LExpr_Var("UnsignedSatQ18__6"),'00000000000000000000000000000000'); +Stmt_Assign(LExpr_Var("UnsignedSatQ19__6"),Expr_Var("TRUE")) +],[],[ +Stmt_Assign(LExpr_Var("UnsignedSatQ18__6"),Expr_TApply("sub_bits.0",[32],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(0,32)]);Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(0,32)])])); +Stmt_Assign(LExpr_Var("UnsignedSatQ19__6"),Expr_Var("FALSE")) +]) +Stmt_If(Expr_Var("UnsignedSatQ19__6"),[ +Stmt_Assign(LExpr_Var("FPSR"),Expr_TApply("append_bits.0",[4;28],[Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(28,4)]);Expr_TApply("append_bits.0",[1;27],['1';Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(0,27)])])])) +],[],[]) +Stmt_VarDeclsNoInit(Type_Bits(32),["UnsignedSatQ35__6"]) +Stmt_VarDeclsNoInit(Type_Constructor("boolean"),["UnsignedSatQ36__6"]) +Stmt_If(Expr_TApply("slt_bits.0",[64],[Expr_TApply("sub_bits.0",[64],[Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(32,32)]);64]);Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(32,32)]);64])]);'0000000000000000000000000000000000000000000000000000000000000000']),[ +Stmt_Assign(LExpr_Var("UnsignedSatQ35__6"),'00000000000000000000000000000000'); +Stmt_Assign(LExpr_Var("UnsignedSatQ36__6"),Expr_Var("TRUE")) +],[],[ +Stmt_Assign(LExpr_Var("UnsignedSatQ35__6"),Expr_TApply("sub_bits.0",[32],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(32,32)]);Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(32,32)])])); +Stmt_Assign(LExpr_Var("UnsignedSatQ36__6"),Expr_Var("FALSE")) +]) +Stmt_If(Expr_Var("UnsignedSatQ36__6"),[ +Stmt_Assign(LExpr_Var("FPSR"),Expr_TApply("append_bits.0",[4;28],[Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(28,4)]);Expr_TApply("append_bits.0",[1;27],['1';Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(0,27)])])])) +],[],[]) +Stmt_VarDeclsNoInit(Type_Bits(32),["UnsignedSatQ51__6"]) +Stmt_VarDeclsNoInit(Type_Constructor("boolean"),["UnsignedSatQ52__6"]) +Stmt_If(Expr_TApply("slt_bits.0",[64],[Expr_TApply("sub_bits.0",[64],[Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(64,32)]);64]);Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(64,32)]);64])]);'0000000000000000000000000000000000000000000000000000000000000000']),[ +Stmt_Assign(LExpr_Var("UnsignedSatQ51__6"),'00000000000000000000000000000000'); +Stmt_Assign(LExpr_Var("UnsignedSatQ52__6"),Expr_Var("TRUE")) +],[],[ +Stmt_Assign(LExpr_Var("UnsignedSatQ51__6"),Expr_TApply("sub_bits.0",[32],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(64,32)]);Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(64,32)])])); +Stmt_Assign(LExpr_Var("UnsignedSatQ52__6"),Expr_Var("FALSE")) +]) +Stmt_If(Expr_Var("UnsignedSatQ52__6"),[ +Stmt_Assign(LExpr_Var("FPSR"),Expr_TApply("append_bits.0",[4;28],[Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(28,4)]);Expr_TApply("append_bits.0",[1;27],['1';Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(0,27)])])])) +],[],[]) +Stmt_VarDeclsNoInit(Type_Bits(32),["UnsignedSatQ67__6"]) +Stmt_VarDeclsNoInit(Type_Constructor("boolean"),["UnsignedSatQ68__6"]) +Stmt_If(Expr_TApply("slt_bits.0",[64],[Expr_TApply("sub_bits.0",[64],[Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(96,32)]);64]);Expr_TApply("ZeroExtend.0",[32;64],[Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(96,32)]);64])]);'0000000000000000000000000000000000000000000000000000000000000000']),[ +Stmt_Assign(LExpr_Var("UnsignedSatQ67__6"),'00000000000000000000000000000000'); +Stmt_Assign(LExpr_Var("UnsignedSatQ68__6"),Expr_Var("TRUE")) +],[],[ +Stmt_Assign(LExpr_Var("UnsignedSatQ67__6"),Expr_TApply("sub_bits.0",[32],[Expr_Slices(Expr_Array(Expr_Var("_Z"),1),[Slice_LoWd(96,32)]);Expr_Slices(Expr_Array(Expr_Var("_Z"),2),[Slice_LoWd(96,32)])])); +Stmt_Assign(LExpr_Var("UnsignedSatQ68__6"),Expr_Var("FALSE")) +]) +Stmt_If(Expr_Var("UnsignedSatQ68__6"),[ +Stmt_Assign(LExpr_Var("FPSR"),Expr_TApply("append_bits.0",[4;28],[Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(28,4)]);Expr_TApply("append_bits.0",[1;27],['1';Expr_Slices(Expr_Var("FPSR"),[Slice_LoWd(0,27)])])])) +],[],[]) +Stmt_Assign(LExpr_Array(LExpr_Var("_Z"),3),Expr_TApply("append_bits.0",[32;96],[Expr_Var("UnsignedSatQ67__6");Expr_TApply("append_bits.0",[32;64],[Expr_Var("UnsignedSatQ51__6");Expr_TApply("append_bits.0",[32;32],[Expr_Var("UnsignedSatQ35__6");Expr_Var("UnsignedSatQ18__6")])])])) diff --git a/cranelift/isle/veri/aslp/tests/parse.rs b/cranelift/isle/veri/aslp/tests/parse.rs new file mode 100644 index 000000000000..ee7a02125f03 --- /dev/null +++ b/cranelift/isle/veri/aslp/tests/parse.rs @@ -0,0 +1,9 @@ +use std::fs; + +use cranelift_isle_veri_test_macros::file_tests; + +#[file_tests(path = "tests/data", ext = "aslt")] +fn parse(test_file: &str) { + let src = fs::read_to_string(test_file).unwrap(); + cranelift_isle_veri_aslp::parser::parse(&src).unwrap(); +} diff --git a/cranelift/isle/veri/docs/language.md b/cranelift/isle/veri/docs/language.md new file mode 100644 index 000000000000..0b7d15aca3c3 --- /dev/null +++ b/cranelift/isle/veri/docs/language.md @@ -0,0 +1,391 @@ +# Specification Language + +Description of specification language. + +## Types + +ISLE types have a corresponding _model_ in the verification domain: + +``` +(model (type )) +``` + +Verification types `` may be primitives, named, or compound types. + +_Primitives_: + +* Integer: `Int` +* Boolean: `Bool` +* Bit-vector: unknown width `(bv)`, fixed width `(bv )` +* Unit: `Unit` +* Unspecified: `!` +* Auto: `_`, a primitive type to be deduced by type inference + +> [!NOTE] +> The unspecified type exists to allow placeholder type specifications when a +> type must be specified to proceed, but it is irrelevant to the problem at hand. +> For example, an enum type may bring into scope variants with new types that are +> not important but need some specification. + +_Named_ type references resolve to the same verification domain type model as +``: + +``` +(named ) +``` + +_Structs_ are purely structurally typed: + +``` +(struct + ( ) + ( ) + ... +) +``` + +_Enum_ types exist in the verification domain but may only be inferred from +corresponding ISLE enum types. Custom enum types cannot be declared by users, +though it is allowed to override the inferred enum type for an ISLE enum with a +custom non-enum model. + +## Specifications + +Term specifications take the form: + +``` +(spec ( ) + (modifies ?) + (provide ) + (require ) + (match ) +) +``` + +All `` lists in the specification must be boolean and for each clause +multiple expressions are wrapped in an implicit `(and )`. + +`(modifies ?)`: +concerns state modification, discussed in the "State" section below. + +`(provide )`: +post-conditions for the term. Post-conditions are assumed when the term appears +as a callee, and asserted when as a caller (root of rule expansion). + +`(require )`: +pre-conditions for the term. Pre-conditions are asserted when the term appears +as a callee, and assumed when as a caller (root of rule expansion). + +`(match )`: +may only be present on specs for _partial_ terms: non-infallible extractors or +partial constructors. Partial terms may be thought of as implicitly returning +an `Option` type, and the match clause specifies the conditions under which the +return is `Some(..)`. In this case, the provide specification is conditioned on +the match specification holding. + +Variables accessible to spec expressions depend on the term type and the clause. +For a term with parameters `( )` and implicit result in special +`result` variable: + +* Constructor: inputs are `[]`, outputs are `[result]` +* Extractor: inputs are `[result]`, inputs are `[]` + +Variables in scope: + +* Term inputs are available to all clauses. +* Term outputs are only available to the `provide` clause. +* State variables are global and available to all clauses. +* Modifies condition variables are available to all clauses. + +### Expressions + +Specification expressions may be: + +**Constants:** +integer ``, bitvector `#b` or `#x`, and booleans +`true`/`false`. + +**Variables:** +plain identifiers refer to in-scope variables. Variables may refer to: term +parameters, the implicit `result` of a term, let or with bindings, macro +arguments, declared state, and state modification path conditions. + +**Operators:** +operator applications of the form `( )`. Available operators are +listed in the next section. + +**Let bindings:** +let bindings introduce new variables with expression initializers, and evaluate +to a body expression that may reference the new variables brought into scope. +``` +(let + ( + ( ) + ( ) + ... + ) + +) +``` +Let bindings may not shadow variables in the outer scope. + +**With bindings:** +`with` expressions evaluate an expression with new _uninitialized_ variables +brought into scope. +``` +(with ( ...) + +) +``` + +**Field Access:** +the expression `(: )` accesses field `` of the struct-valued expression ``. + +**Discriminator:** +expression `(? )` evaluates to true if the enum-valued expression +`` has the given variant. + +**Variant Constructor:** +`(. )` constructs an enum value with the given variant +and (optional) fields. + +**Struct Constructor:** +`(struct ( ) ...)` constructs a struct value with the given fields. + +**Match:** +the match operator pattern matches on enum types. + +``` +(match + ((. ) ) + ((. ) ) + ... +) +``` + +The value of the expression is the body of the arm that matches ``, +evaluated with the fields brought into scope. If no arm matches the value is +undefined. + +> [!WARNING] +> Under the hood `match` and `switch` are treated differently. Match is a +> top-level expression type, while `switch` is an operator. This makes no sense +> and should be fixed. It makes no difference to the user, however. + +**Macro Expansion:** +`(! )` evaluates macro `` with the given arguments. + +**Qualified Expressions:** +`(as )` evaluates to `` and provides a type inference annotation that +`` must have type ``. + +### Operators + +Spec expression operators: + +``` + // Boolean operations + Eq, + And, + Or, + Not, + Imp, + + // Integer comparisons + Lt, + Lte, + Gt, + Gte, + + // Bitwise bitvector operations (directly SMT-LIB) + BVNot, + BVAnd, + BVOr, + BVXor, + + // Bitvector arithmetic operations (directly SMT-LIB) + BVNeg, + BVAdd, + BVSub, + BVMul, + BVUdiv, + BVUrem, + BVSdiv, + BVSrem, + BVShl, + BVLshr, + BVAshr, + + // Bitvector comparison operations (directly SMT-LIB) + BVUle, + BVUlt, + BVUgt, + BVUge, + BVSlt, + BVSle, + BVSgt, + BVSge, + + // Bitvector overflow checks (SMT-LIB pending standardization) + BVSaddo, + + // Desugared bitvector arithmetic operations + Rotr, + Rotl, + Extract, + ZeroExt, + SignExt, + Concat, + + // Floating point (IEEE 754-2008) + FPPositiveInfinity, + FPNegativeInfinity, + FPPositiveZero, + FPNegativeZero, + FPNaN, + FPAdd, + FPSub, + FPMul, + FPDiv, + FPMin, + FPMax, + FPNeg, + FPSqrt, + FPIsZero, + FPIsInfinite, + FPIsNaN, + FPIsNegative, + FPIsPositive, + + // Custom encodings + Popcnt, + Clz, + Cls, + Rev, + + // Conversion operations + ConvTo, + Int2BV, + BV2Nat, + WidthOf, + + // Control operations + If, + Switch, +``` + +### Macros + +Spec macros may be declared: + +``` +(macro ( ) ) +``` + +Macro expansions are of the form `(! )`. The body of the macro is +evaluated in a scope with paramters set to argument values, and the result +substituted for the expansion expression. + +## Type Instantiation + +Possible type signatures for a term may be enumerated with `instantiate`: + +``` +(instantiate ) +``` + +where term signatures are of the form: + +``` +((args ) (ret )) +``` + +Since some type instantiations are common, sets of signatures may be declared as `form`s: + +``` +(form ) +``` + +and then referenced as short-hand in an `instantiate` declaration: + +``` +(instantiate ) +``` + +In verification, the cartesian product of all type instantantiations for all +present terms is considered. Many of the combinations will be ruled out by type +inference before proceeding to verification. + +## State + +State variables are declared with a type and default specification: + +``` +(state + (type ) + (default ) +) +``` + +The `` is a verification domain type as discussed above. The default spec +is an expression that must have boolean value. It is evaluated in a scope with +the state variable bound to variable ``. + +State variables are accessible as global variables from specs. The `modifies` +clause on specs determines the conditions under which the default spec is +applied: + +`(modifies )`: +declare that a spec unconditionally modifies the state variable ``. The +default spec for `` is disabled. + +`(modifies )`: +conditionally modify `` with conditional variable ``. In this +case, the corresponding spec must provide constraints that define when `` +holds, and implied constraints on `` if it does. The default spec for +`` will only apply if `` is false. Unconditional state modification +is equivalent to conditional state modification with an assertion that `` +is always true. + +In verification, all the conditional variables for a given state are collected ``, `` and the default spec is conditionally assumed: + +``` +(=> (not (or ...)) ) +``` + +## Attributes + +Attributes may be applied to terms and rules: + +``` +(attr rule? ) +``` + +Without the `rule` keyword, it is assumed to be a term attribute. + +Attribute kinds: + +`(attr (veri chain))`: +In verification, apply rule chaining to this term. A term marked for chaining +may omit a specification. Instead, all possible applications of rules to this +term will be generated and verified. + +`(attr rule (veri priority))`: +In verification, declare that the correctness of lower priority rules depends on +this rule not matching. + +During rule expansion, any higher-priority overlapping rules that have the +priority tag will have their match conditions negated and added to the +verification conditions. + +Note that care must be taken when using this tag: if the specification for the +match conditions of the higher priority rule are an over-approximation of +reality, then the assumptions made by lower priority rules will be an +under-approximation. In an extreme case this may cause the verifier to determine +the lower priority rule never applies. In a more subtle case, it could cause +bugs to be missed. + +`(attr rule? (tag ))`: +Tag attributes allow for categorizing terms and rules. They have no semantic +meaning but are useful for filtering verification in the command-line and +presenting aggregate verification status. diff --git a/cranelift/isle/veri/isaspec/Cargo.toml b/cranelift/isle/veri/isaspec/Cargo.toml new file mode 100644 index 000000000000..4cac1d7aa0b8 --- /dev/null +++ b/cranelift/isle/veri/isaspec/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "cranelift-isle-veri-isaspec" +version = "0.1.0" +publish = false +edition.workspace = true + +[dependencies] +cranelift-isle-veri-aslp = { path = "../aslp" } +cranelift-isle = { path = "../../isle" } +cranelift-codegen = { workspace = true, features = ["all-arch"] } +reqwest = { workspace = true } +anyhow = { workspace = true, features = ['std', 'backtrace'] } +clap = { workspace = true, features = ['default'] } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +itertools = { workspace = true } diff --git a/cranelift/isle/veri/isaspec/script/generate.sh b/cranelift/isle/veri/isaspec/script/generate.sh new file mode 100755 index 000000000000..5740482e2256 --- /dev/null +++ b/cranelift/isle/veri/isaspec/script/generate.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +# Options +function usage() { + echo "Usage: ${0} [-h] [-l] [-a ] [-p ] [-o ]" + exit 2 +} + +launch_server="false" +aslp_server_host="${ASLP_SERVER_HOST:-127.0.0.1}" +aslp_server_port="${ASLP_SERVER_PORT:-4207}" +output_path="../../../codegen/src/isa/aarch64/spec/" +while getopts "la:p:o:h" opt; do + case "${opt}" in + l) launch_server="true" ;; + a) aslp_server_host="${OPTARG}" ;; + p) aslp_server_port="${OPTARG}" ;; + o) output_path="${OPTARG}" ;; + h) usage ;; + *) usage ;; + esac +done + +# Floating-point constant specs. +cargo run --bin fpconst > "${output_path}/fp_const.isle" + +# Launch server +if [[ "${launch_server}" == "true" ]]; then + aslp-server --host "${aslp_server_host}" --port "${aslp_server_port}" & + aslp_server_pid=$! + trap 'kill "${aslp_server_pid}"' EXIT +fi + +# Generate +aslp_server_url="http://${aslp_server_host}:${aslp_server_port}" +cargo run --bin isaspec \ + -- \ + --server "${aslp_server_url}" \ + --output "${output_path}" diff --git a/cranelift/isle/veri/isaspec/src/aarch64.rs b/cranelift/isle/veri/isaspec/src/aarch64.rs new file mode 100644 index 000000000000..edc7a093f5e7 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/aarch64.rs @@ -0,0 +1,115 @@ +use cranelift_codegen::{ + MachBuffer, MachInstEmit, + isa::aarch64, + isa::aarch64::inst::{ + Inst, + emit::{EmitInfo, EmitState}, + }, + settings, +}; + +use crate::{ + constraints::{Scope, Target}, + memory::{ReadEffect, SetEffect}, +}; + +pub fn gpreg(i: usize) -> Target { + let r = Target::Var("_R".to_string()); + Target::Index(Box::new(r), i) +} + +pub fn vreg(i: usize) -> Target { + let z = Target::Var("_Z".to_string()); + Target::Index(Box::new(z), i) +} + +pub fn literal(s: &str) -> Target { + Target::Var(s.to_string()) +} + +pub fn pstate() -> Target { + Target::Var("PSTATE".to_string()) +} + +pub fn pstate_field(name: &str) -> Target { + Target::Field(Box::new(pstate()), name.to_string()) +} + +pub fn fpcr() -> Target { + Target::Var("FPCR".to_string()) +} + +pub fn state() -> Scope { + let mut scope = Scope::new(); + + // Boolean literals + for lit in ["FALSE", "TRUE"] { + scope.global(literal(lit)); + } + + // Memory effects. + let read_effect = ReadEffect::new(); + for target in read_effect.targets() { + scope.global(target.clone()); + } + + let set_effect = SetEffect::new(); + for target in set_effect.targets() { + scope.global(target.clone()); + } + + // General purpose register file. + for i in 0..31 { + scope.global(gpreg(i)); + } + + // Vector register file. + for i in 0..31 { + scope.global(vreg(i)); + } + + // NZCV + for field in &["N", "Z", "C", "V"] { + scope.global(pstate_field(field)); + } + + // FPCR + scope.global(fpcr()); + + scope +} + +/// Assemble the instruction to machine code bytes. +pub fn assemble(inst: &Inst) -> Vec { + let flags = settings::Flags::new(settings::builder()); + let isa_flags = aarch64::settings::Flags::new(&flags, &aarch64::settings::builder()); + let emit_info = EmitInfo::new(flags, isa_flags); + let mut buffer = MachBuffer::new(); + inst.emit(&mut buffer, &emit_info, &mut Default::default()); + let buffer = buffer.finish(&Default::default(), &mut Default::default()); + buffer.data().to_vec() +} + +/// Assemble the instruction and partition into opcodes. +pub fn opcodes(inst: &Inst) -> Vec { + let machine_code = assemble(inst); + let mut opcodes = Vec::new(); + for opcode_bytes in machine_code.chunks(4) { + assert_eq!(opcode_bytes.len(), 4); + opcodes.push(u32::from_le_bytes(opcode_bytes.try_into().unwrap())); + } + opcodes +} + +/// Assemble the instruction and returns the single opcode. Errors if the +/// instruction is not represented by a single opcode. +pub fn opcode(inst: &Inst) -> u32 { + let opcodes = opcodes(inst); + assert_eq!(opcodes.len(), 1); + opcodes[0] +} + +/// Assembly for the given instruction. +pub fn assembly(inst: &Inst) -> String { + inst.print_with_state(&mut EmitState::default()) +} diff --git a/cranelift/isle/veri/isaspec/src/bin/constraints.rs b/cranelift/isle/veri/isaspec/src/bin/constraints.rs new file mode 100644 index 000000000000..f8530137b996 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/bin/constraints.rs @@ -0,0 +1,410 @@ +use std::vec; + +use anyhow::Result; +use clap::Parser as ClapParser; +use cranelift_codegen::ir::types::I64; +use cranelift_codegen::isa::aarch64::inst::{ + ALUOp, ALUOp3, BitOp, Cond, FPUOp1, FPUOp2, Imm12, ImmLogic, ImmShift, Inst, MoveWideConst, + MoveWideOp, NZCV, OperandSize, ScalarSize, ShiftOp, ShiftOpAndAmt, ShiftOpShiftImm, VecALUOp, + VecMisc2, VectorSize, vreg, writable_vreg, writable_xreg, xreg, +}; +use cranelift_isle::printer; +use cranelift_isle_veri_aslp::ast::Block; +use cranelift_isle_veri_aslp::client::Client; +use tracing::debug; + +use cranelift_isle_veri_isaspec::aarch64; +use cranelift_isle_veri_isaspec::constraints::Translator; +use cranelift_isle_veri_isaspec::semantics::inst_semantics; + +#[derive(ClapParser)] +#[command(version, about)] +struct Args { + /// Server URL + #[arg(long = "server", required = true)] + server: String, + + /// Print debugging output (repeat for more detail) + #[arg(short = 'd', long = "debug", action = clap::ArgAction::Count)] + debug_level: u8, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + // Setup tracing output. + tracing_subscriber::fmt() + .with_timer(tracing_subscriber::fmt::time::uptime()) + .with_level(true) + .with_target(false) + .with_max_level(match args.debug_level { + 0 => tracing::Level::WARN, + 1 => tracing::Level::INFO, + 2 => tracing::Level::DEBUG, + _ => tracing::Level::TRACE, + }) + .init(); + + // ASLp client. + let http_client = reqwest::blocking::Client::new(); + let client = Client::new(&http_client, args.server)?; + + // Conversion. + let insts = define_insts(); + for inst in &insts { + println!("-------------------------------------"); + let opcode = aarch64::opcode(inst); + let asm = aarch64::assembly(inst); + println!("inst = {inst:#?}"); + println!("opcode = {opcode:08x}"); + println!("asm = {asm}"); + println!("----"); + let block = inst_semantics(inst, &client)?; + convert_block(&block)?; + println!("-------------------------------------"); + } + + Ok(()) +} + +// Define instructions to test. +fn define_insts() -> Vec { + let mut insts = Vec::new(); + + // OperandSize + let sizes = [OperandSize::Size32, OperandSize::Size64]; + + // AluRRR + let alu_ops = vec![ + ALUOp::Add, + ALUOp::Sub, + ALUOp::Orr, + ALUOp::OrrNot, + ALUOp::And, + ALUOp::AndNot, + ALUOp::Eor, + ALUOp::EorNot, + ALUOp::AddS, + ALUOp::SubS, + ALUOp::SMulH, + ALUOp::UMulH, + ALUOp::Adc, + ALUOp::Sbc, + ALUOp::AdcS, + ALUOp::SbcS, + ALUOp::Lsr, + ALUOp::Asr, + ALUOp::Lsl, + ALUOp::Extr, + ALUOp::SDiv, + ALUOp::UDiv, + ]; + for alu_op in alu_ops { + insts.push(Inst::AluRRR { + alu_op, + size: OperandSize::Size64, + rd: writable_xreg(4), + rn: xreg(5), + rm: xreg(6), + }); + } + + // AluRRImm12 + let alu_ops_imm12 = [ALUOp::Add, ALUOp::Sub, ALUOp::AddS, ALUOp::SubS]; + let imm12_vals = [0x000123u64, 0x123000u64]; + for alu_op in alu_ops_imm12 { + for imm12_val in imm12_vals { + let imm12 = Imm12::maybe_from_u64(imm12_val).unwrap(); + insts.push(Inst::AluRRImm12 { + alu_op, + size: OperandSize::Size64, + rd: writable_xreg(4), + rn: xreg(5), + imm12, + }); + } + } + + // AluRRImmLogic + let alu_ops_imml = [ALUOp::And, ALUOp::EorNot]; + let imml_vals = [0xf003fffff003ffffu64, 0xffffffffff000000u64]; + for alu_op in alu_ops_imml { + for imml_val in imml_vals { + let imml = ImmLogic::maybe_from_u64(imml_val, I64).unwrap(); + insts.push(Inst::AluRRImmLogic { + alu_op, + size: OperandSize::Size64, + rd: writable_xreg(4), + rn: xreg(5), + imml, + }); + } + } + + // AluRRImmShift + let alu_ops_immshift = [ALUOp::Lsr, ALUOp::Lsl]; + let immshift_vals = [13u64, 62]; + for alu_op in alu_ops_immshift { + for immshift_val in immshift_vals { + let immshift = ImmShift::maybe_from_u64(immshift_val).unwrap(); + insts.push(Inst::AluRRImmShift { + alu_op, + size: OperandSize::Size64, + rd: writable_xreg(4), + rn: xreg(5), + immshift, + }); + } + } + + // AluRRRShift + let alu_ops_rrr_shift = [ALUOp::Add, ALUOp::And]; + let shiftops = [ShiftOp::LSL, ShiftOp::ASR]; + let amts = [13u64, 63]; + for alu_op in alu_ops_rrr_shift { + for shiftop in shiftops { + for amt in amts { + let shiftop = + ShiftOpAndAmt::new(shiftop, ShiftOpShiftImm::maybe_from_shift(amt).unwrap()); + insts.push(Inst::AluRRRShift { + alu_op, + size: OperandSize::Size64, + rd: writable_xreg(4), + rn: xreg(5), + rm: xreg(6), + shiftop, + }); + } + } + } + + // AluRRRR + let alu_ops = vec![ALUOp3::MAdd, ALUOp3::MSub, ALUOp3::UMAddL, ALUOp3::SMAddL]; + for alu_op in alu_ops { + insts.push(Inst::AluRRRR { + alu_op, + size: OperandSize::Size32, + rd: writable_xreg(4), + rn: xreg(1), + rm: xreg(2), + ra: xreg(3), + }); + } + + // BitRR + let ops = vec![ + BitOp::RBit, + BitOp::Clz, + BitOp::Cls, + BitOp::Rev16, + BitOp::Rev32, + BitOp::Rev64, + ]; + for op in ops { + insts.push(Inst::BitRR { + op, + size: OperandSize::Size64, + rd: writable_xreg(2), + rn: xreg(1), + }); + } + + // MovWide + let mov_wide_ops = [MoveWideOp::MovN, MoveWideOp::MovZ]; + let values = [0x00001234u64, 0x12340000u64]; + for mov_wide_op in mov_wide_ops { + for size in sizes { + for value in values { + insts.push(Inst::MovWide { + op: mov_wide_op, + rd: writable_xreg(4), + imm: MoveWideConst::maybe_from_u64(value).unwrap(), + size, + }); + } + } + } + + // CSel + let conds = vec![ + Cond::Eq, + Cond::Ne, + Cond::Hs, + Cond::Lo, + Cond::Mi, + Cond::Pl, + Cond::Vs, + Cond::Vc, + Cond::Hi, + Cond::Ls, + Cond::Ge, + Cond::Lt, + Cond::Gt, + Cond::Le, + Cond::Al, + Cond::Nv, + ]; + for cond in conds.clone() { + insts.push(Inst::CSel { + rd: writable_xreg(3), + cond, + rn: xreg(1), + rm: xreg(2), + }); + } + + // CSNeg + for cond in conds.clone() { + insts.push(Inst::CSNeg { + rd: writable_xreg(3), + cond, + rn: xreg(1), + rm: xreg(2), + }); + } + + // CCmp + for cond in conds.clone() { + insts.push(Inst::CCmp { + size: OperandSize::Size64, + rn: xreg(1), + rm: xreg(2), + nzcv: NZCV::new(true, false, true, false), + cond, + }); + } + + // FpuCmp + insts.push(Inst::FpuCmp { + size: ScalarSize::Size64, + rn: vreg(1), + rm: vreg(2), + }); + + // FpuRR + let fpu_op1s = [FPUOp1::Neg]; + for fpu_op1 in fpu_op1s { + insts.push(Inst::FpuRR { + fpu_op: fpu_op1, + size: ScalarSize::Size64, + rd: writable_vreg(1), + rn: vreg(2), + }); + } + + // FpuRRR + let fpu_op2s = [FPUOp2::Add, FPUOp2::Sub]; + for fpu_op2 in fpu_op2s { + insts.push(Inst::FpuRRR { + fpu_op: fpu_op2, + size: ScalarSize::Size64, + rd: writable_vreg(1), + rn: vreg(2), + rm: vreg(3), + }); + } + + // VecRRR + let alu_ops = vec![ + VecALUOp::Cmeq, + VecALUOp::Cmge, + VecALUOp::Cmgt, + VecALUOp::Cmhs, + VecALUOp::Cmhi, + VecALUOp::And, + VecALUOp::Bic, + VecALUOp::Orr, + VecALUOp::Umaxp, + VecALUOp::Add, + VecALUOp::Sub, + VecALUOp::Mul, + VecALUOp::Sshl, + VecALUOp::Ushl, + VecALUOp::Umin, + VecALUOp::Smin, + VecALUOp::Umax, + VecALUOp::Smax, + VecALUOp::Urhadd, + VecALUOp::Addp, + VecALUOp::Zip1, + VecALUOp::Zip2, + VecALUOp::Uzp1, + VecALUOp::Uzp2, + VecALUOp::Trn1, + VecALUOp::Trn2, + // TODO: 128-bit bitvector literal + // VecALUOp::Eor, + // TODO: boolean literals + // VecALUOp::Sqadd, + // VecALUOp::Uqadd, + // VecALUOp::Sqsub, + // VecALUOp::Uqsub, + // VecALUOp::Sqrdmulh, + // TODO: floating point. + // VecALUOp::Fcmeq, + // VecALUOp::Fcmgt, + // VecALUOp::Fcmge, + // VecALUOp::Fadd, + // VecALUOp::Fsub, + // VecALUOp::Fdiv, + // VecALUOp::Fmax, + // VecALUOp::Fmin, + // VecALUOp::Fmul, + ]; + for alu_op in alu_ops { + insts.push(Inst::VecRRR { + alu_op, + rd: writable_vreg(3), + rn: vreg(1), + rm: vreg(2), + size: VectorSize::Size32x4, + }); + } + + // VecMisc + let vec_misc2s = vec![VecMisc2::Cnt]; + for vec_misc2 in vec_misc2s { + insts.push(Inst::VecMisc { + op: vec_misc2, + rd: writable_vreg(3), + rn: vreg(1), + size: VectorSize::Size8x8, + }); + } + + insts +} + +// Convert a semantics block and print the result. +fn convert_block(block: &Block) -> Result<()> { + // Translation. + let mut translator = Translator::new(aarch64::state(), "v".to_string()); + translator.translate(block)?; + + // Report. + let global = translator.global(); + debug!("scope: {global:#?}"); + + let init = global.init(); + let bindings = global.bindings(); + + for r in global.reads() { + println!("read:\t{r}\t{}", init[r]); + } + + for w in global.writes() { + println!( + "write:\t{w}\t{}", + bindings[w].as_var().expect("binding should be variable") + ); + } + + println!(); + + for constraint in global.constraints() { + printer::dump(constraint).unwrap(); + println!(); + } + + Ok(()) +} diff --git a/cranelift/isle/veri/isaspec/src/bin/fpconst.rs b/cranelift/isle/veri/isaspec/src/bin/fpconst.rs new file mode 100644 index 000000000000..152b4b1ec62d --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/bin/fpconst.rs @@ -0,0 +1,121 @@ +struct Case { + in_bits: u8, + signed: bool, + out_bits: u8, + bits: u64, +} + +impl Case { + fn from_f32(signed: bool, out_bits: u8, x: f32) -> Self { + Self { + in_bits: 32, + signed, + out_bits, + bits: x.to_bits() as u64, + } + } + + fn from_f64(signed: bool, out_bits: u8, x: f64) -> Self { + Self { + in_bits: 64, + signed, + out_bits, + bits: x.to_bits(), + } + } + + fn cond(&self) -> String { + format!( + "(and (= in #x{:02x}) (= signed {}) (= out #x{:02x}))", + self.in_bits, self.signed, self.out_bits, + ) + } +} + +fn print_spec(name: &str, cases: Vec) { + println!("(spec ({name} signed in out)"); + + println!("\t(provide"); + for case in &cases { + println!("\t\t(=> {} (= result #x{:016x}))", case.cond(), case.bits); + } + println!("\t)"); + + println!("\t(require (or"); + for case in &cases { + println!("\t\t{}", case.cond()); + } + println!("\t))"); + + println!(")"); +} + +fn min_fp_value() { + print_spec( + "min_fp_value", + vec![ + // f32 signed + Case::from_f32(true, 8, i8::MIN as f32 - 1.), + Case::from_f32(true, 16, i16::MIN as f32 - 1.), + Case::from_f32(true, 32, i32::MIN as f32), + Case::from_f32(true, 64, i64::MIN as f32), + // f32 unsigned + Case::from_f32(false, 8, -1.0), + Case::from_f32(false, 16, -1.0), + Case::from_f32(false, 32, -1.0), + Case::from_f32(false, 64, -1.0), + // f64 signed + Case::from_f64(true, 8, i8::MIN as f64 - 1.), + Case::from_f64(true, 16, i16::MIN as f64 - 1.), + Case::from_f64(true, 32, i32::MIN as f64 - 1.), + Case::from_f64(true, 64, i64::MIN as f64), + // f64 unsigned + Case::from_f64(false, 8, -1.0), + Case::from_f64(false, 16, -1.0), + Case::from_f64(false, 32, -1.0), + Case::from_f64(false, 64, -1.0), + ], + ); +} + +fn max_fp_value() { + print_spec( + "max_fp_value", + vec![ + // f32 signed + Case::from_f32(true, 8, i8::MAX as f32 + 1.), + Case::from_f32(true, 16, i16::MAX as f32 + 1.), + Case::from_f32(true, 32, (i32::MAX as u64 + 1) as f32), + Case::from_f32(true, 64, (i64::MAX as u64 + 1) as f32), + // f32 unsigned + Case::from_f32(false, 8, u8::MAX as f32 + 1.), + Case::from_f32(false, 16, u16::MAX as f32 + 1.), + Case::from_f32(false, 32, (u32::MAX as u64 + 1) as f32), + Case::from_f32(false, 64, (u64::MAX as u128 + 1) as f32), + // f64 signed + Case::from_f64(true, 8, i8::MAX as f64 + 1.), + Case::from_f64(true, 16, i16::MAX as f64 + 1.), + Case::from_f64(true, 32, i32::MAX as f64 + 1.), + Case::from_f64(true, 64, (i64::MAX as u64 + 1) as f64), + // f64 unsigned + Case::from_f64(false, 8, u8::MAX as f64 + 1.), + Case::from_f64(false, 16, u16::MAX as f64 + 1.), + Case::from_f64(false, 32, u32::MAX as f64 + 1.), + Case::from_f64(false, 64, (u64::MAX as u128 + 1) as f64), + ], + ); +} + +fn generate() { + println!(";; GENERATED BY `fpconst`. DO NOT EDIT!!!"); + + println!(); + min_fp_value(); + + println!(); + max_fp_value(); +} + +pub fn main() { + generate(); +} diff --git a/cranelift/isle/veri/isaspec/src/bin/isaspec.rs b/cranelift/isle/veri/isaspec/src/bin/isaspec.rs new file mode 100644 index 000000000000..eb5565a0a5a4 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/bin/isaspec.rs @@ -0,0 +1,97 @@ +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::{io, vec}; + +use anyhow::Result; +use clap::Parser as ClapParser; + +use cranelift_isle::{ + ast::{Attr, AttrKind, AttrTarget, Def}, + printer, +}; +use cranelift_isle_veri_aslp::client::Client; +use cranelift_isle_veri_isaspec::{builder::Builder, instructions, spec::spec_ident}; + +#[derive(ClapParser)] +#[command(version, about)] +struct Args { + /// Server URL + #[arg(long = "server", required = true)] + server: String, + + // Output directory. + #[arg(long, required = true)] + output: PathBuf, + + // Maximum line width. + #[arg(long, default_value = "120")] + width: usize, + + /// Print debugging output (repeat for more detail) + #[arg(short = 'd', long = "debug", action = clap::ArgAction::Count)] + debug_level: u8, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + // Setup tracing output. + tracing_subscriber::fmt() + .with_writer(io::stderr) + .with_timer(tracing_subscriber::fmt::time::uptime()) + .with_level(true) + .with_target(false) + .with_max_level(match args.debug_level { + 0 => tracing::Level::WARN, + 1 => tracing::Level::INFO, + 2 => tracing::Level::DEBUG, + _ => tracing::Level::TRACE, + }) + .init(); + + // ASLp client. + let http_client = reqwest::blocking::Client::new(); + let client = Client::new(&http_client, args.server)?; + + // Conversion. + let file_configs = instructions::define()?; + for file_config in file_configs { + // Generate specs. + let mut defs = Vec::new(); + for spec_config in file_config.specs { + // Tag the term as generated. + defs.push(Def::Attr(generated_attribute(&spec_config.term))); + // Build and output the spec. + let builder = Builder::new(spec_config, &client); + let def = builder.build()?; + defs.push(def); + } + + // Output. + let path = args.output.join(file_config.name); + write_spec(&path, &defs, args.width)?; + } + + Ok(()) +} + +fn generated_attribute(term: &str) -> Attr { + Attr { + target: AttrTarget::Term(spec_ident(term.to_string())), + kinds: vec![AttrKind::Tag(spec_ident("isaspec_generated".to_string()))], + pos: Default::default(), + } +} + +fn write_spec(path: &Path, defs: &[Def], width: usize) -> Result<()> { + let mut output = std::fs::File::create(path)?; + + // Code generation warning. + writeln!(output, ";; GENERATED BY `isaspec`. DO NOT EDIT!!!")?; + writeln!(output)?; + + // Format with ISLE printer. + printer::print(defs, width, &mut output)?; + + Ok(()) +} diff --git a/cranelift/isle/veri/isaspec/src/bits.rs b/cranelift/isle/veri/isaspec/src/bits.rs new file mode 100644 index 000000000000..a491fb79abfb --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/bits.rs @@ -0,0 +1,241 @@ +use std::{collections::HashMap, ops::Range}; + +use anyhow::{Result, bail, format_err}; +use cranelift_isle_veri_aslp::opcode::{self, Opcode}; + +#[derive(Clone)] +pub struct Bits { + pub segments: Vec, +} + +impl Bits { + pub fn empty() -> Self { + Bits { + segments: Vec::new(), + } + } + + pub fn from_u32(x: u32) -> Self { + Bits { + segments: vec![Segment::from_u32(x)], + } + } + + pub fn is_symbolic(&self) -> bool { + self.segments.iter().any(|s| s.is_symbolic()) + } + + pub fn width(&self) -> usize { + self.segments.iter().map(|s| s.width()).sum() + } + + /// Evaluate the bitvector template with the given assignment. + pub fn eval(&self, assignment: &HashMap) -> Result { + let mut result = 0u32; + let mut offset = 0usize; + for segment in &self.segments { + let value = match segment { + Segment::Symbolic(name, _) => assignment.get(name).ok_or(format_err!( + "missing assignment for symbolic segment: {}", + name + ))?, + Segment::Constant(c, _) => c, + }; + result |= value << offset; + offset += segment.width() + } + Ok(result) + } + + pub fn splice(base: &Bits, insert: &Bits, offset: usize) -> Result { + let mut result = Bits::empty(); + if offset > 0 { + let prefix = base.extract(0, offset)?; + result.append(prefix); + } + result.append(insert.clone()); + if result.width() < base.width() { + let suffix = base.extract(result.width(), base.width())?; + result.append(suffix); + } + Ok(result) + } + + pub fn append(&mut self, other: Bits) { + self.segments.extend(other.segments); + } + + pub fn extract(&self, lo: usize, hi: usize) -> Result { + let mut result = Bits::empty(); + let mut offset = 0usize; + for segment in &self.segments { + // Intersection of this interval with extraction interval. + let start = std::cmp::max(lo, offset); + let end = std::cmp::min(hi, offset + segment.width()); + + // If the intersection is non-empty, add a segment. + if start < end { + result + .segments + .push(segment.extract(start - offset, end - offset)?); + } + + // Advance offset. + offset += segment.width(); + } + Ok(result) + } +} + +impl std::fmt::Display for Bits { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.segments + .iter() + .rev() + .map(|s| s.to_string()) + .collect::>() + .join(" ") + ) + } +} + +impl From for opcode::Opcode { + fn from(bits: Bits) -> Self { + Opcode { + segments: bits.segments.into_iter().map(Into::into).collect(), + } + } +} + +/// Concrete assignment of symbolic bits in a bitvector template. +pub struct Concrete { + pub assignment: HashMap, + pub template: Bits, +} + +impl Concrete { + pub fn eval(&self) -> Result { + self.template.eval(&self.assignment) + } +} + +pub struct ConcreteIterator { + fields: Vec<(String, u32)>, + bits: Range, + template: Bits, +} + +impl ConcreteIterator { + fn new(template: Bits) -> Self { + let mut fields = Vec::new(); + let mut n = 1; + for segment in &template.segments { + match segment { + Segment::Symbolic(name, width) => { + fields.push((name.clone(), (*width).try_into().unwrap())); + n <<= width; + } + Segment::Constant(_, _) => {} + } + } + + ConcreteIterator { + fields, + bits: 0..n, + template, + } + } +} + +impl Iterator for ConcreteIterator { + type Item = Concrete; + + fn next(&mut self) -> Option { + // Advance to next assignment of all symbolic bits. + let mut bits = self.bits.next()?; + + // Divide into individual fields. + let mut assignment = HashMap::new(); + for (name, width) in &self.fields { + let mask = (1 << width) - 1; + assignment.insert(name.clone(), bits & mask); + bits >>= width; + } + + Some(Concrete { + assignment, + template: self.template.clone(), + }) + } +} + +impl IntoIterator for &Bits { + type Item = Concrete; + type IntoIter = ConcreteIterator; + + fn into_iter(self) -> Self::IntoIter { + ConcreteIterator::new(self.clone()) + } +} + +#[derive(Clone)] +pub enum Segment { + Symbolic(String, usize), + Constant(u32, usize), +} + +impl Segment { + pub fn from_u32(x: u32) -> Self { + Segment::Constant(x, 32) + } + + pub fn is_symbolic(&self) -> bool { + matches!(self, Segment::Symbolic(_, _)) + } + + pub fn width(&self) -> usize { + match self { + Segment::Symbolic(_, w) | Segment::Constant(_, w) => *w, + } + } + + pub fn extract(&self, lo: usize, hi: usize) -> Result { + match *self { + Segment::Symbolic(_, w) => { + if !(lo == 0 && hi == w) { + bail!("symbolic segments must remain whole"); + } + Ok(self.clone()) + } + Segment::Constant(c, w) => { + if !(lo < hi && hi <= w) { + bail!("invalid extraction interval"); + } + let w = hi - lo; + let mask = (1 << w) - 1; + Ok(Segment::Constant((c >> lo) & mask, w)) + } + } + } +} + +impl std::fmt::Display for Segment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Segment::Symbolic(s, w) => write!(f, "{s}:{w}"), + Segment::Constant(c, w) => write!(f, "{c:#x}:{w}"), + } + } +} + +impl From for opcode::Segment { + fn from(segment: Segment) -> Self { + match segment { + Segment::Symbolic(name, width) => opcode::Segment::Symbolic(name, width), + Segment::Constant(value, width) => opcode::Segment::Constant(value, width), + } + } +} diff --git a/cranelift/isle/veri/isaspec/src/builder.rs b/cranelift/isle/veri/isaspec/src/builder.rs new file mode 100644 index 000000000000..d4d6c69a9fa3 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/builder.rs @@ -0,0 +1,350 @@ +//! Construction of VeriISLE specifications from ASLp semantics. + +use std::collections::{HashMap, HashSet}; +use std::vec; + +use anyhow::{Result, bail}; +use itertools::Itertools; + +use cranelift_codegen::isa::aarch64::inst::Inst; +use cranelift_isle::ast::{self, Def, Modifies, Spec, SpecExpr}; +use cranelift_isle::lexer::Pos; +use cranelift_isle_veri_aslp::client::Client; + +use crate::bits::Bits; +use crate::constraints::{Binding, Scope, Target, Translator}; +use crate::spec::spec_field; +use crate::{ + aarch64, + spec::{Conditions, spec_all, spec_ident, spec_idents, spec_var, spec_with, substitute}, +}; + +pub struct SpecConfig { + pub term: String, + pub args: Vec, + pub cases: Cases, +} + +#[allow(clippy::large_enum_variant, reason = "verification code")] +pub enum Cases { + Instruction(InstConfig), + Cases(Vec), + Match(Match), +} + +pub struct Case { + pub conds: Vec, + pub cases: Cases, +} + +pub struct Match { + pub on: SpecExpr, + pub arms: Vec, +} + +pub struct Arm { + pub variant: String, + pub args: Vec, + pub body: Cases, +} + +#[derive(Clone)] +pub enum Expectation { + Require, + Allow, +} + +#[derive(Clone)] +pub struct Mapping { + expr: SpecExpr, + expect: Expectation, + modifies: Vec, +} + +impl Mapping { + pub fn new(expr: SpecExpr, expect: Expectation) -> Self { + Self { + expr, + expect, + modifies: Vec::new(), + } + } + + pub fn require(expr: SpecExpr) -> Self { + Self::new(expr, Expectation::Require) + } + + pub fn allow(expr: SpecExpr) -> Self { + Self::new(expr, Expectation::Allow) + } +} + +#[derive(Clone)] +pub struct MappingBuilder(Mapping); + +impl MappingBuilder { + pub fn new(expr: SpecExpr) -> Self { + Self(Mapping::new(expr, Expectation::Require)) + } + + pub fn var(name: &str) -> Self { + Self::new(spec_var(name.to_string())) + } + + pub fn state(name: &str) -> Self { + Self::new(spec_var(name.to_string())).modifies(name) + } + + pub fn field(mut self, field: &str) -> Self { + self.0.expr = spec_field(field.to_string(), self.0.expr); + self + } + + pub fn allow(mut self) -> Self { + self.0.expect = Expectation::Allow; + self + } + + pub fn modifies(mut self, state: &str) -> Self { + self.0.modifies.push(state.to_string()); + self + } + + pub fn build(self) -> Mapping { + self.0 + } +} + +#[derive(Clone, Default)] +pub struct Mappings { + pub reads: HashMap, + pub writes: HashMap, +} + +impl Mappings { + fn required_reads(&self) -> HashSet { + Self::required_targets(&self.reads) + } + + fn required_writes(&self) -> HashSet { + Self::required_targets(&self.writes) + } + + fn required_targets(target_mapping: &HashMap) -> HashSet { + target_mapping + .iter() + .filter_map(|(target, mapping)| match mapping.expect { + Expectation::Require => Some(target.clone()), + Expectation::Allow => None, + }) + .collect() + } +} + +pub enum Opcodes { + Instruction(Inst), + Template(Bits), +} + +impl Opcodes { + pub fn bits(&self) -> Bits { + match self { + Opcodes::Instruction(inst) => { + let opcode = aarch64::opcode(inst); + Bits::from_u32(opcode) + } + Opcodes::Template(bits) => bits.clone(), + } + } +} + +pub struct InstConfig { + pub opcodes: Opcodes, + pub scope: Scope, + pub mappings: Mappings, +} + +pub struct Builder<'a> { + cfg: SpecConfig, + client: &'a Client<'a>, +} + +impl<'a> Builder<'a> { + pub fn new(cfg: SpecConfig, client: &'a Client<'a>) -> Self { + Self { cfg, client } + } + + pub fn build(&self) -> Result { + let spec = self.spec()?; + let def = Def::Spec(spec); + Ok(def) + } + + fn spec(&self) -> Result { + let cond = self.cases(&self.cfg.cases)?; + let modifies = spec_idents(&cond.modifies.iter().sorted().cloned().collect::>()); + let spec = Spec { + term: spec_ident(self.cfg.term.clone()), + args: spec_idents(&self.cfg.args), + requires: cond.requires, + provides: cond.provides, + matches: Vec::new(), + modifies: modifies + .into_iter() + .map(|state| Modifies { state, cond: None }) + .collect(), + pos: Pos::default(), + }; + Ok(spec) + } + + fn cases(&self, cases: &Cases) -> Result { + match cases { + Cases::Instruction(case) => self.case(case), + Cases::Cases(cases) => { + let conds = cases + .iter() + .map(|case| { + let mut cond = self.cases(&case.cases)?; + cond.requires.extend(case.conds.clone()); + Ok(cond) + }) + .collect::>>()?; + Ok(Conditions::merge(conds)) + } + Cases::Match(m) => { + let mut require_arms = Vec::new(); + let mut arms = Vec::new(); + let mut modifies = HashSet::new(); + for arm in &m.arms { + // Build conditions for the arm body. + let cond = self.cases(&arm.body)?; + + // Provides form the body of the arm. + arms.push(ast::Arm { + variant: spec_ident(arm.variant.clone()), + args: spec_idents(&arm.args), + body: spec_all(cond.provides), + pos: Pos::default(), + }); + + // This arm requires a match on the variant, as well as + // requirements from the body. + require_arms.push(ast::Arm { + variant: spec_ident(arm.variant.clone()), + args: spec_idents(&arm.args), + body: spec_all(cond.requires), + pos: Pos::default(), + }); + + // Merge modifies. + modifies.extend(cond.modifies); + } + + Ok(Conditions { + requires: vec![SpecExpr::Match { + x: Box::new(m.on.clone()), + arms: require_arms, + pos: Pos::default(), + }], + provides: vec![SpecExpr::Match { + x: Box::new(m.on.clone()), + arms, + pos: Pos::default(), + }], + modifies, + }) + } + } + } + + fn case(&self, case: &InstConfig) -> Result { + // Semantics. + let opcode_bits = case.opcodes.bits(); + let block = self.client.opcode(opcode_bits.into())?; + + // Translation. + let mut translator = Translator::new(case.scope.clone(), "t".to_string()); + translator.translate(&block)?; + + let global = translator.global(); + + // Reads mapping. + let mut substitutions = HashMap::new(); + let mut modifies = HashSet::new(); + let reads = global.reads(); + let init = global.init(); + for target in reads.iter().sorted() { + // Expect mapping for the read. + let Some(mapping) = case.mappings.reads.get(target) else { + bail!("read of {target} is unmapped"); + }; + + // Lookup variable holding the initial read value. + let v = &init[target]; + + // Substitute variable for mapped expression. + substitutions.insert(v.clone(), mapping.expr.clone()); + + // Read operations should not modify state. + if !mapping.modifies.is_empty() { + bail!("read of {target} should not modify state"); + } + } + + if let Some(target) = case.mappings.required_reads().difference(reads).next() { + bail!("{target} should have been read"); + } + + // Writes mapping. + let writes = global.writes(); + let bindings = global.bindings(); + for target in writes.iter().sorted() { + // Expect mapping for the write. + let Some(mapping) = case.mappings.writes.get(target) else { + bail!("write to {target} is unmapped"); + }; + + // Lookup bound variable. + let Some(Binding::Var(v)) = bindings.get(target) else { + bail!("{target} not bound to variable"); + }; + + // Substitute variable for mapped expression. + substitutions.insert(v.clone(), mapping.expr.clone()); + + // Update modifies list. + modifies.extend(mapping.modifies.clone()); + } + + if let Some(target) = case.mappings.required_writes().difference(writes).next() { + bail!("{target} should have been written"); + } + + // Finalize provided constraints. + let mut provides = Vec::new(); + for constraint in global.constraints() { + provides.push(substitute(constraint.clone(), &substitutions)?); + } + + // Determine remaining temporaries and encapsulate in a scope. + let temporaries: Vec<_> = global + .vars() + .iter() + .filter(|v| !substitutions.contains_key(*v)) + .sorted() + .cloned() + .collect(); + if !temporaries.is_empty() { + let with_scope = spec_with(spec_idents(&temporaries), spec_all(provides)); + provides = vec![with_scope]; + } + + // Conditions. + Ok(Conditions { + requires: Vec::new(), + provides, + modifies, + }) + } +} diff --git a/cranelift/isle/veri/isaspec/src/configure.rs b/cranelift/isle/veri/isaspec/src/configure.rs new file mode 100644 index 000000000000..c731f1139bff --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/configure.rs @@ -0,0 +1,246 @@ +use std::collections::HashMap; + +use anyhow::{Result, bail}; +use cranelift_codegen::{Reg, RegClass, isa::aarch64::inst::Inst}; + +use crate::{ + aarch64::{self, pstate_field}, + bits::Bits, + builder::{MappingBuilder, Mappings}, + constraints::Target, + spec::{spec_as_bit_vector_width, spec_conv_to, spec_var}, +}; +use cranelift_isle::ast::SpecExpr; + +#[macro_export(local_inner_macros)] +macro_rules! spec_config { + (($family:ident $($arg:ident)+) { $($conf:tt)* }) => { + __mappings! { @init mappings $($conf)* }; + SpecConfig { + term: std::concat!("MInst.", std::stringify!($family)).to_string(), + args: [$(std::stringify!($arg),)+] + .map(String::from) + .to_vec(), + cases: __cases! { + ( + family: $family, + args: ($($arg)+), + mappings: mappings + ), + $($conf)* + }, + } + }; +} + +#[macro_export(local_inner_macros)] +macro_rules! __mappings { + (@init $mappings:ident $($conf:tt)*) => { + let mut $mappings = Mappings::default(); + // TODO: move boolean literals to a library function. + $mappings + .reads + .insert(aarch64::literal("TRUE"), Mapping::allow(spec_true())); + $mappings + .reads + .insert(aarch64::literal("FALSE"), Mapping::allow(spec_false())); + __mappings! { @conf $mappings $($conf)* } + }; + + // Read and write mappings for general-purpose registers. + (@conf $mappings:ident register ($name:ident, read, gp, $id:literal); $($conf:tt)*) => { + $mappings.reads.insert( + aarch64::gpreg($id), + Mapping::require(spec_var(std::stringify!($name).to_string())), + ); + let $name = xreg($id); + __mappings! { @conf $mappings $($conf)* } + }; + + (@conf $mappings:ident register ($name:ident, read, gp64, $id:literal); $($conf:tt)*) => { + $mappings.reads.insert( + aarch64::gpreg($id), + Mapping::require(spec_as_bit_vector_width(spec_var(std::stringify!($name).to_string()), 64)), + ); + let $name = xreg($id); + __mappings! { @conf $mappings $($conf)* } + }; + + (@conf $mappings:ident register ($name:ident, write, gp, $id:literal); $($conf:tt)*) => { + $mappings.writes.insert( + aarch64::gpreg($id), + Mapping::require(spec_var(std::stringify!($name).to_string())), + ); + let $name = writable_xreg($id); + __mappings! { @conf $mappings $($conf)* } + }; + + // Read and write mappings for floating-point registers. + (@conf $mappings:ident register ($name:ident, read, fp, $id:literal); $($conf:tt)*) => { + $mappings.reads.insert( + aarch64::vreg($id), + Mapping::require($crate::configure::spec_fp_reg(std::stringify!($name))), + ); + let $name = vreg($id); + __mappings! { @conf $mappings $($conf)* } + }; + + (@conf $mappings:ident register ($name:ident, write, fp, $id:literal); $($conf:tt)*) => { + $mappings.writes.insert( + aarch64::vreg($id), + Mapping::require($crate::configure::spec_fp_reg(std::stringify!($name))), + ); + let $name = writable_vreg($id); + __mappings! { @conf $mappings $($conf)* } + }; + + (@conf $mappings:ident fpcr (); $($conf:tt)*) => { + $mappings + .reads + .insert(aarch64::fpcr(), MappingBuilder::var("fpcr").allow().build()); + __mappings! { @conf $mappings $($conf)* } + }; + + // Read and write mappings for full vector/floating-point registers. + (@conf $mappings:ident register ($name:ident, read, vec, $id:literal); $($conf:tt)*) => { + $mappings.reads.insert( + aarch64::vreg($id), + Mapping::require(spec_var(std::stringify!($name).to_string())), + ); + let $name = vreg($id); + __mappings! { @conf $mappings $($conf)* } + }; + + (@conf $mappings:ident register ($name:ident, write, vec, $id:literal); $($conf:tt)*) => { + $mappings.writes.insert( + aarch64::vreg($id), + Mapping::require(spec_var(std::stringify!($name).to_string())), + ); + let $name = writable_vreg($id); + __mappings! { @conf $mappings $($conf)* } + }; + + // Flags mappings. + (@conf $mappings:ident flags (); $($conf:tt)*) => { + $crate::configure::configure_flags_mappings(&mut $mappings); + __mappings! { @conf $mappings $($conf)* } + }; + + (@conf $mappings:ident $directive:ident $args:tt; $($conf:tt)*) => { + __mappings! { @conf $mappings $($conf)* } + }; + + (@conf $mappings:ident) => {}; +} + +#[macro_export(local_inner_macros)] +macro_rules! __cases { + ($meta:tt, enumerate ($it:ident, $arms:ident); $($tt:tt)+) => { + Cases::Match(Match { + on: spec_var(std::stringify!($it).to_string()), + arms: $arms + .iter() + .copied() + .flat_map(|$it| { + let body = __cases! { $meta, $($tt)* }; + Some(Arm { + variant: std::format!("{:?}", $it), + args: Vec::new(), + body, + }) + }).collect(), + }) + }; + + ($meta:tt, filter ($expr:expr); $($tt:tt)+) => { + if $expr { + __cases! { $meta, $($tt)* } + } else { + return None; + } + }; + + ((family: $family:ident, args: ($($arg:ident)+), mappings: $mappings:ident), instruction ();) => { + Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(Inst::$family{ + $($arg,)+ + }), + scope: aarch64::state(), + mappings: $mappings.clone(), + }) + }; + + ($meta:tt, $directive:ident $args:tt; $($tt:tt)*) => { + __cases! { $meta, $($tt)* } + }; +} + +pub fn flags_mappings() -> Mappings { + let mut mappings = Mappings::default(); + configure_flags_mappings(&mut mappings); + mappings +} + +pub fn configure_flags_mappings(mappings: &mut Mappings) { + // Instruction model is the MInst value itself, which is considered the result of the variant term. + let inst = MappingBuilder::var("result").allow(); + + // Input and output flags of the instruction are fields of the MInst model. + let flags_in = inst.clone().field("flags_in"); + let flags_out = inst.clone().field("flags_out"); + + // Construct read and write mappings for each NZCV field. + for field in &["N", "Z", "C", "V"] { + // Read + mappings + .reads + .insert(pstate_field(field), flags_in.clone().field(field).build()); + + // Write + mappings + .writes + .insert(pstate_field(field), flags_out.clone().field(field).build()); + } +} + +// Spec expression for the lower 64 bits of a 128-bit floating-point register. +pub fn spec_fp_reg(name: &str) -> SpecExpr { + spec_conv_to( + 128, + spec_as_bit_vector_width(spec_var(name.to_string()), 64), + ) +} + +// Compare an opcode template against the instruction we expect it to represent. +pub fn verify_opcode_template(template: &Bits, expect: F) -> Result<()> +where + F: Fn(&HashMap) -> Result, +{ + // Iterate over all template values. + for concrete in template.into_iter() { + let inst = expect(&concrete.assignment)?; + let opcode = aarch64::opcode(&inst); + let got = concrete.eval()?; + if got != opcode { + bail!( + "template mismatch: opcode {:#x}, template {:#x}", + opcode, + got, + ); + } + } + Ok(()) +} + +// Convert a Cranelift register to the corresponding element of AArch64 state in +// ASLp. +pub fn reg_target(reg: Reg) -> Result { + let Some(preg) = reg.to_real_reg() else { + bail!("not physical register") + }; + let index = preg.hw_enc().into(); + Ok(match preg.class() { + RegClass::Int => aarch64::gpreg(index), + RegClass::Float | RegClass::Vector => aarch64::vreg(index), + }) +} diff --git a/cranelift/isle/veri/isaspec/src/constraints.rs b/cranelift/isle/veri/isaspec/src/constraints.rs new file mode 100644 index 000000000000..b944dc74e3e1 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/constraints.rs @@ -0,0 +1,821 @@ +//! Translation of ASLp semantics to constraints. + +use core::{fmt, panic}; +use std::cmp::Ordering; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::vec; + +use anyhow::{Result, bail, format_err}; +use cranelift_isle::ast::{SpecExpr, SpecOp}; +use cranelift_isle::lexer::Pos; +use cranelift_isle_veri_aslp::ast::{Block, Expr, Func, LExpr, Slice, Stmt}; +use tracing::debug; + +use crate::memory::{ReadEffect, SetEffect}; +use crate::spec::*; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)] +pub enum Target { + Var(String), + Index(Box, usize), + Field(Box, String), +} + +impl fmt::Display for Target { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Var(v) => write!(f, "{v}"), + Self::Index(a, i) => write!(f, "{a}[{i}]"), + Self::Field(s, field) => write!(f, "{s}.{field}"), + } + } +} + +impl TryFrom<&LExpr> for Target { + type Error = anyhow::Error; + + fn try_from(lexpr: &LExpr) -> Result { + match lexpr { + LExpr::Var(v) => Ok(Target::Var(v.clone())), + LExpr::ArrayIndex { array, index } => { + let array = Box::new(array.as_ref().try_into()?); + let index = index + .as_lit_int() + .ok_or(format_err!("array index must be literal integer"))? + .parse()?; + Ok(Target::Index(array, index)) + } + LExpr::Field { x, name } => { + let x = Box::new(x.as_ref().try_into()?); + Ok(Target::Field(x, name.clone())) + } + } + } +} + +impl TryFrom<&Expr> for Target { + type Error = anyhow::Error; + + fn try_from(expr: &Expr) -> Result { + match expr { + Expr::Var(v) => Ok(Target::Var(v.clone())), + Expr::ArrayIndex { array, index } => { + let array = Box::new(array.as_ref().try_into()?); + let index = index + .as_lit_int() + .ok_or(format_err!("array index must be literal integer"))? + .parse()?; + Ok(Target::Index(array, index)) + } + Expr::Field { x, name } => { + let x = Box::new(x.as_ref().try_into()?); + Ok(Target::Field(x, name.clone())) + } + _ => todo!("target expr: {expr:?}"), + } + } +} + +#[derive(Debug, Clone)] +pub enum Binding { + Uninitialized, + Global, + Var(String), +} + +impl Binding { + pub fn as_var(&self) -> Option<&String> { + match self { + Binding::Var(v) => Some(v), + _ => None, + } + } +} + +#[derive(Debug, Clone)] +pub struct Scope { + constraints: Vec, + vars: HashSet, + decls: HashSet, + bindings: BTreeMap, + init: HashMap, + reads: HashSet, + writes: HashSet, +} + +impl Default for Scope { + fn default() -> Self { + Self::new() + } +} + +impl Scope { + pub fn new() -> Self { + Self { + constraints: Vec::new(), + vars: HashSet::new(), + decls: HashSet::new(), + bindings: BTreeMap::new(), + init: HashMap::new(), + reads: HashSet::new(), + writes: HashSet::new(), + } + } + + pub fn constraints(&self) -> &Vec { + &self.constraints + } + + fn constrain(&mut self, constraint: SpecExpr) { + self.constraints.push(constraint); + } + + pub fn vars(&self) -> &HashSet { + &self.vars + } + + pub fn reads(&self) -> &HashSet { + &self.reads + } + + pub fn writes(&self) -> &HashSet { + &self.writes + } + + pub fn init(&self) -> &HashMap { + &self.init + } + + pub fn bindings(&self) -> &BTreeMap { + &self.bindings + } + + fn bind(&mut self, target: Target, b: Binding) { + self.bindings.insert(target.clone(), b); + } + + fn decl(&mut self, target: Target) { + self.decls.insert(target.clone()); + self.bind(target, Binding::Uninitialized); + } + + pub fn global(&mut self, target: Target) { + self.decls.insert(target.clone()); + self.bind(target, Binding::Global); + } + + fn add_var(&mut self, v: String) { + self.vars.insert(v); + } + + fn bind_var(&mut self, target: Target, v: String) { + self.add_var(v.clone()); + self.bind(target, Binding::Var(v)); + } + + fn init_var(&mut self, target: Target, v: String) { + assert!(!self.init.contains_key(&target)); + self.init.insert(target.clone(), v.clone()); + self.bind_var(target, v); + } + + fn write(&mut self, target: Target, v: String) { + self.writes.insert(target.clone()); + self.bind_var(target, v); + } + + fn read(&mut self, target: &Target) -> Option<&Binding> { + if let Some(b) = self.bindings.get(target) { + debug!(?target, "scope read"); + self.reads.insert(target.clone()); + return Some(b); + } + None + } + + fn update(&mut self, child: &Self) { + self.constraints.extend(child.constraints.iter().cloned()); + self.vars.extend(child.vars.iter().cloned()); + for target in &child.writes { + if !child.decls.contains(target) { + self.bind(target.clone(), child.bindings[target].clone()); + self.writes.insert(target.clone()); + } + } + } +} + +struct VariableAllocator { + index: usize, + prefix: String, +} + +impl VariableAllocator { + fn new(prefix: String) -> Self { + Self { index: 0, prefix } + } + + fn alloc(&mut self) -> String { + let index = self.index; + self.index += 1; + format!("{}{}", self.prefix, index) + } +} + +pub struct Translator { + stack: Vec, + vars: VariableAllocator, +} + +impl Translator { + pub fn new(global: Scope, prefix: String) -> Self { + Self { + stack: vec![global], + vars: VariableAllocator::new(prefix), + } + } + + pub fn global(&self) -> &Scope { + self.stack.first().expect("stack must be non-empty") + } + + fn enter(&mut self) { + debug!("enter scope"); + self.stack.push(Scope::new()) + } + + fn exit(&mut self) { + let scope = self.pop(); + debug!(?scope, "exit scope"); + self.scope_mut().update(&scope); + } + + fn pop(&mut self) -> Scope { + self.stack.pop().expect("stack must be non-empty") + } + + fn scope_mut(&mut self) -> &mut Scope { + self.stack.last_mut().expect("stack must be non-empty") + } + + fn constrain(&mut self, constraint: SpecExpr) { + self.scope_mut().constrain(constraint) + } + + fn write(&mut self, target: &Target, v: &str) { + self.scope_mut().write(target.clone(), v.to_string()); + } + + fn read(&mut self, target: &Target) -> Result { + // Read from innermost scope. + for scope in self.stack.iter_mut().rev() { + match scope.read(target) { + None => continue, + Some(Binding::Var(v)) => return Ok(v.clone()), + Some(Binding::Uninitialized) => bail!("uninitialized read: {target}"), + Some(Binding::Global) => { + let v = self.vars.alloc(); + scope.init_var(target.clone(), v.clone()); + return Ok(v); + } + }; + } + let scope = self.scope_mut(); + debug!(?scope, "scope"); + bail!("undefined read: {target}") + } + + pub fn translate(&mut self, block: &Block) -> Result<()> { + self.enter(); + self.block(block)?; + self.exit(); + Ok(()) + } + + fn block(&mut self, block: &Block) -> Result<()> { + for stmt in &block.stmts { + self.stmt(stmt)?; + } + Ok(()) + } + + fn stmt(&mut self, stmt: &Stmt) -> Result<()> { + match stmt { + Stmt::Assign { lhs, rhs } => { + let target = lhs.try_into()?; + let rhs = self.expr(rhs)?; + self.assign(&target, rhs) + } + Stmt::ConstDecl { name, rhs, .. } | Stmt::VarDecl { name, rhs, .. } => { + let target = Target::Var(name.clone()); + self.scope_mut().decl(target.clone()); + let rhs = self.expr(rhs)?; + self.assign(&target, rhs) + } + Stmt::VarDeclsNoInit { names, .. } => { + for name in names { + let target = Target::Var(name.clone()); + self.scope_mut().decl(target); + } + Ok(()) + } + Stmt::Assert { cond } => { + let constraint = self.expr(cond)?; + self.constrain(constraint); + Ok(()) + } + Stmt::If { + cond, + then_block, + else_block, + } => { + self.enter(); + + // Assign the conditional to a variable. + let cond = self.expr(cond)?; + let c = self.bind(cond)?; + let cond = spec_var(c); + + // Execute then. Pop off the scope. + self.enter(); + self.block(then_block)?; + let then_scope = self.pop(); + + // Execute else. Pop off the scope. + self.enter(); + self.block(else_block)?; + let else_scope = self.pop(); + + // Join blocks. + self.constrain(spec_if( + cond.clone(), + spec_all(then_scope.constraints), + spec_all(else_scope.constraints), + )); + + // Merge target bindings. + let mut targets = BTreeSet::new(); + targets.extend(then_scope.bindings.keys()); + targets.extend(else_scope.bindings.keys()); + for target in targets { + let (t, e) = match ( + then_scope.bindings.get(target), + else_scope.bindings.get(target), + ) { + (Some(Binding::Var(t)), Some(Binding::Var(e))) => (t.clone(), e.clone()), + (Some(Binding::Var(t)), None) => (t.clone(), self.read(target)?), + (None, Some(Binding::Var(e))) => (self.read(target)?, e.clone()), + _ => bail!("unable to merge conditional scopes"), + }; + let phi = spec_if(cond.clone(), spec_var(t.clone()), spec_var(e.clone())); + self.assign(target, phi)?; + } + + // Merge additional scope metadata. + let joined = self.scope_mut(); + joined.vars.extend(then_scope.vars.iter().cloned()); + joined.vars.extend(else_scope.vars.iter().cloned()); + + joined.reads.extend(then_scope.reads.iter().cloned()); + joined.reads.extend(else_scope.reads.iter().cloned()); + + joined.writes.extend(then_scope.writes.iter().cloned()); + joined.writes.extend(else_scope.writes.iter().cloned()); + + // Exit if scope. + self.exit(); + + Ok(()) + } + Stmt::Call { + func, + types: _, + args, + } => self.call(func, args), + } + } + + fn assign(&mut self, target: &Target, rhs: SpecExpr) -> Result<()> { + // Bind the expression to a variable. + let v = self.bind(rhs)?; + + // Write variable to the target. + self.write(target, &v); + + Ok(()) + } + + // Bind expression to a variable and return it. + fn bind(&mut self, expr: SpecExpr) -> Result { + let v = self.vars.alloc(); + self.scope_mut().add_var(v.clone()); + let lhs = spec_var(v.clone()); + self.constrain(spec_eq(lhs, expr)); + Ok(v) + } + + fn expr(&mut self, expr: &Expr) -> Result { + match expr { + Expr::Apply { func, types, args } => self.func(func, types, args), + Expr::Var(..) | Expr::ArrayIndex { .. } | Expr::Field { .. } => { + let target: Target = expr.try_into()?; + Ok(spec_var(self.read(&target)?)) + } + Expr::Slices { x, slices } => { + let slice = expect_unary(slices)?; + Ok(self.slice(x, slice)?) + } + Expr::LitBits(bits) => { + let val = u128::from_str_radix(bits, 2)?; + let width = bits.len(); + Ok(spec_const_bit_vector(val, width)) + } + Expr::LitInt(v) => { + let val = v.parse()?; + Ok(spec_const_int(val)) + } + } + } + + fn func(&mut self, func: &Func, types: &[Expr], args: &[Expr]) -> Result { + match func.name.as_str() { + "ZeroExtend" => { + let (x, w) = expect_binary(args)?; + let x = self.expr(x)?; + let w = expect_lit_int_as_usize(w)?; + Ok(spec_zero_ext(w, x)) + } + "SignExtend" => { + let (x, w) = expect_binary(args)?; + let x = self.expr(x)?; + let w = expect_lit_int_as_usize(w)?; + Ok(spec_sign_ext(w, x)) + } + "not_bool" => { + let x = expect_unary(args)?; + let x = self.expr(x)?; + Ok(spec_unary(SpecOp::Not, x)) + } + "append_bits" => { + let (x, y) = expect_binary(args)?; + let x = self.expr(x)?; + let y = self.expr(y)?; + Ok(spec_binary(SpecOp::Concat, x, y)) + } + "replicate_bits" => { + let (x, n) = expect_binary(args)?; + let x = self.expr(x)?; + let n = expect_lit_int_as_usize(n)?; + Ok(spec_binary( + SpecOp::Replicate, + x, + spec_const_int(n.try_into()?), + )) + } + "not_bits" => { + let x = expect_unary(args)?; + let x = self.expr(x)?; + Ok(spec_unary(SpecOp::BVNot, x)) + } + "cvt_bool_bv" => { + let b = expect_unary(args)?; + let b = self.expr(b)?; + Ok(spec_if( + b, + spec_const_bit_vector(1, 1), + spec_const_bit_vector(0, 1), + )) + } + "cvt_bits_uint" => { + let x = expect_unary(args)?; + let x = self.expr(x)?; + Ok(spec_bv2nat(x)) + } + "ite" => { + let (c, t, e) = expect_ternary(args)?; + Ok(spec_if(self.expr(c)?, self.expr(t)?, self.expr(e)?)) + } + "and_bool" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::And, lhs, rhs)) + } + "eq_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::Eq, lhs, rhs)) + } + "ne_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_unary(SpecOp::Not, spec_binary(SpecOp::Eq, lhs, rhs))) + } + "add_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVAdd, lhs, rhs)) + } + "sub_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVSub, lhs, rhs)) + } + "or_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVOr, lhs, rhs)) + } + "and_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVAnd, lhs, rhs)) + } + "eor_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVXor, lhs, rhs)) + } + "mul_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVMul, lhs, rhs)) + } + "sdiv_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVSdiv, lhs, rhs)) + } + "lsr_bits" => self.shift(SpecOp::BVLshr, types, args), + "asr_bits" => self.shift(SpecOp::BVAshr, types, args), + "lsl_bits" => self.shift(SpecOp::BVShl, types, args), + "sle_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVSle, lhs, rhs)) + } + "slt_bits" => { + let (lhs, rhs) = expect_binary(args)?; + let lhs = self.expr(lhs)?; + let rhs = self.expr(rhs)?; + Ok(spec_binary(SpecOp::BVSlt, lhs, rhs)) + } + "Mem.read" => { + let (addr, size, access) = expect_ternary(args)?; + self.mem_read(addr, size, access) + } + "FPAdd" | "FPSub" | "FPMul" | "FPDiv" | "FPMin" | "FPMax" | "FPCompare" | "FPSqrt" + | "FPRoundInt" => self.primitive(&func.name, args), + "FPConvert" | "FixedToFP" | "FPToFixed" => { + self.primitive_with_types(&func.name, types, args) + } + unexpected => todo!("func: {unexpected}"), + } + } + + fn call(&mut self, func: &Func, args: &[Expr]) -> Result<()> { + match func.name.as_str() { + "Mem.set" => { + let (addr, size, access, value) = expect_quaternary(args)?; + self.mem_set(addr, size, access, value) + } + unexpected => todo!("call: {unexpected}"), + } + } + + fn slice(&mut self, x: &Expr, slice: &Slice) -> Result { + match slice { + Slice::LowWidth(l, w) => { + let l = expect_lit_int_as_usize(l)?; + let w = expect_lit_int_as_usize(w)?; + let h = l + w - 1; + let x = self.expr(x)?; + Ok(spec_extract(h, l, x)) + } + } + } + + fn shift(&mut self, op: SpecOp, types: &[Expr], args: &[Expr]) -> Result { + // Map input and shift to spec expressions. + let (x, s) = expect_binary(args)?; + let x = self.expr(x)?; + let mut s = self.expr(s)?; + + // ASLp maps the shift amount to a bit vector in an integer conversion + // pass, which can result in the shift argument being a different width + // than the input. If so, extend the shift to match. + let (xw, sw) = expect_binary_types(types)?; + match xw.cmp(&sw) { + Ordering::Greater => s = spec_zero_ext(xw, s), + Ordering::Equal => {} + Ordering::Less => panic!("shift argument wider than input"), + } + + Ok(spec_binary(op, x, s)) + } + + fn mem_read(&mut self, addr: &Expr, size: &Expr, access: &Expr) -> Result { + // Map parameters to spec expressions. + let addr = self.expr(addr)?; + let size_bytes = expect_lit_int_as_usize(size)?; + let size_bits = 8 * size_bytes; + Self::check_supported_mem_access(access)?; + + // Memory read operation modifies read effect variables. + let read_effect = ReadEffect::new(); + self.assign(&read_effect.active, spec_true())?; + self.assign( + &read_effect.size_bits, + spec_const_int(size_bits.try_into()?), + )?; + self.assign(&read_effect.addr, addr)?; + + let value = self.read(&read_effect.value)?; + Ok(spec_var(value)) + } + + fn mem_set(&mut self, addr: &Expr, size: &Expr, access: &Expr, value: &Expr) -> Result<()> { + // Map parameters to spec expressions. + let addr = self.expr(addr)?; + let size_bytes = expect_lit_int_as_usize(size)?; + let size_bits = 8 * size_bytes; + Self::check_supported_mem_access(access)?; + let value = self.expr(value)?; + + // Memory set operation modifies set effect variables. + let set_effect = SetEffect::new(); + self.assign(&set_effect.active, spec_true())?; + self.assign(&set_effect.size_bits, spec_const_int(size_bits.try_into()?))?; + self.assign(&set_effect.addr, addr)?; + self.assign(&set_effect.value, value)?; + + Ok(()) + } + + fn check_supported_mem_access(access: &Expr) -> Result<()> { + // Should be a constant integer. + let access = expect_lit_int_as_usize(access)?; + + // Access flags not fully implemented: error on unexpected value. + // + // First two access types: AccType_NORMAL, AccType_VEC. + if access > 1 { + bail!("unsupported memory read access type"); + } + Ok(()) + } + + fn primitive(&mut self, name: &str, args: &[Expr]) -> Result { + Ok(SpecExpr::Expand { + name: spec_ident(name.to_string()), + args: args + .iter() + .map(|arg| self.expr(arg)) + .collect::>()?, + pos: Pos::default(), + }) + } + + fn primitive_with_types( + &mut self, + name: &str, + types: &[Expr], + args: &[Expr], + ) -> Result { + let all_args = args.iter().chain(types); + Ok(SpecExpr::Expand { + name: spec_ident(name.to_string()), + args: all_args.map(|arg| self.expr(arg)).collect::>()?, + pos: Pos::default(), + }) + } +} + +fn expect_unary(xs: &[T]) -> Result<&T> { + if xs.len() != 1 { + bail!("expected unary"); + } + Ok(&xs[0]) +} + +fn expect_binary(xs: &[T]) -> Result<(&T, &T)> { + if xs.len() != 2 { + bail!("expected binary"); + } + Ok((&xs[0], &xs[1])) +} + +fn expect_ternary(xs: &[T]) -> Result<(&T, &T, &T)> { + if xs.len() != 3 { + bail!("expected ternary"); + } + Ok((&xs[0], &xs[1], &xs[2])) +} + +fn expect_quaternary(xs: &[T]) -> Result<(&T, &T, &T, &T)> { + if xs.len() != 4 { + bail!("expected quaternary"); + } + Ok((&xs[0], &xs[1], &xs[2], &xs[3])) +} + +fn expect_binary_types(types: &[Expr]) -> Result<(usize, usize)> { + let (t1, t2) = expect_binary(types)?; + Ok((expect_lit_int_as_usize(t1)?, expect_lit_int_as_usize(t2)?)) +} + +fn expect_lit_int_as_usize(expr: &Expr) -> Result { + let value = eval_int_const(expr) + .ok_or_else(|| format_err!("expected literal integer, got: {expr:?}"))?; + Ok(value.try_into()?) +} + +/// Evaluate an integer-valued ASL expression to a constant, if it reduces to +/// one. +/// +/// ASLp sometimes expresses a statically-constant integer symbolically. For +/// example, the width of the `EXTR` result slice is emitted as +/// `(lsb + datasize - 1) - lsb + 1`, where `lsb` is a symbolic shift amount. +/// The `lsb` terms cancel, leaving the constant `datasize`. To recover the +/// constant we track a linear combination of opaque atoms (the sub-expressions +/// we cannot evaluate, such as the symbolic `lsb`), so that cancelling terms +/// collapse away. Returns `None` if any atom survives with a nonzero +/// coefficient, i.e. the expression is not actually constant. +fn eval_int_const(expr: &Expr) -> Option { + LinearInt::eval(expr).as_const() +} + +/// A linear integer expression: a constant plus a sum of `coefficient * atom` +/// terms, where each atom is an ASL sub-expression we cannot evaluate further. +struct LinearInt { + constant: i128, + /// `(atom, coefficient)` pairs, all with nonzero coefficient. + terms: Vec<(Expr, i128)>, +} + +impl LinearInt { + fn constant(c: i128) -> Self { + Self { + constant: c, + terms: Vec::new(), + } + } + + fn atom(e: &Expr) -> Self { + Self { + constant: 0, + terms: vec![(e.clone(), 1)], + } + } + + /// Add `sign * other` into `self`, combining like atoms and dropping any + /// whose coefficient cancels to zero. + fn add_scaled(&mut self, other: LinearInt, sign: i128) { + self.constant += sign * other.constant; + for (atom, coeff) in other.terms { + match self.terms.iter_mut().find(|(a, _)| *a == atom) { + Some(slot) => slot.1 += sign * coeff, + None => self.terms.push((atom, sign * coeff)), + } + } + self.terms.retain(|(_, coeff)| *coeff != 0); + } + + fn eval(expr: &Expr) -> Self { + match expr { + Expr::LitInt(v) => match v.parse() { + Ok(n) => Self::constant(n), + Err(_) => Self::atom(expr), + }, + Expr::Apply { func, args, .. } => match (func.name.as_str(), args.as_slice()) { + ("add_int", [a, b]) => { + let mut lin = Self::eval(a); + lin.add_scaled(Self::eval(b), 1); + lin + } + ("sub_int", [a, b]) => { + let mut lin = Self::eval(a); + lin.add_scaled(Self::eval(b), -1); + lin + } + _ => Self::atom(expr), + }, + _ => Self::atom(expr), + } + } + + /// The constant value, if no symbolic atoms remain. + fn as_const(&self) -> Option { + self.terms.is_empty().then_some(self.constant) + } +} diff --git a/cranelift/isle/veri/isaspec/src/instructions.rs b/cranelift/isle/veri/isaspec/src/instructions.rs new file mode 100644 index 000000000000..ee3c824428a7 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/instructions.rs @@ -0,0 +1,2347 @@ +use crate::{ + aarch64, + bits::{Bits, Segment}, + builder::{ + Arm, Case, Cases, InstConfig, Mapping, MappingBuilder, Mappings, Match, Opcodes, SpecConfig, + }, + configure::{flags_mappings, reg_target, spec_fp_reg, verify_opcode_template}, + constraints::Target, + memory::{ReadEffect, SetEffect}, + spec::{ + spec_as_bit_vector_width, spec_binary, spec_const_bit_vector, spec_const_int, + spec_discriminator, spec_eq, spec_eq_bool, spec_extract, spec_false, spec_field, spec_true, + spec_var, + }, + spec_config, +}; +use anyhow::Result; +use cranelift_codegen::{ + Reg, Writable, + ir::{MemFlagsData, types::I8}, + isa::aarch64::inst::{ + ALUOp, ALUOp3, AMode, ASIMDFPModImm, ASIMDMovModImm, BitOp, Cond, ExtendOp, + FPULeftShiftImm, FPUOp1, FPUOp2, FPUOpRI, FPUOpRIMod, FPURightShiftImm, FpuRoundMode, + FpuToIntOp, Imm12, Inst, IntToFpuOp, MoveWideConst, MoveWideOp, NZCV, OperandSize, SImm9, + ScalarSize, ShiftOp, ShiftOpAndAmt, ShiftOpShiftImm, UImm5, UImm12Scaled, VecALUOp, + VecLanesOp, VecMisc2, VectorSize, vreg, writable_vreg, writable_xreg, xreg, + }, +}; +use cranelift_isle::ast::{SpecExpr, SpecOp}; +use itertools::Itertools; +use std::collections::HashMap; +use std::path::PathBuf; +use std::vec; + +/// Configuration for an ISLE specification file to generate. +pub struct FileConfig { + pub name: PathBuf, + pub specs: Vec, +} + +/// Define specifications to generate. +pub fn define() -> Result> { + Ok(vec![ + FileConfig { + name: "alu_rrr.isle".into(), + specs: vec![define_alu_rrr()], + }, + FileConfig { + name: "alu_rrrr.isle".into(), + specs: vec![define_alu_rrrr()], + }, + FileConfig { + name: "alu_rr_imm12.isle".into(), + specs: vec![define_alu_rr_imm12()?], + }, + FileConfig { + name: "alu_rrr_shift.isle".into(), + specs: vec![define_alu_rrr_shift()?], + }, + FileConfig { + name: "alu_rrr_extend.isle".into(), + specs: vec![define_alu_rrr_extend()], + }, + FileConfig { + name: "bit_rr.isle".into(), + specs: vec![define_bit_rr()], + }, + FileConfig { + name: "loads.isle".into(), + specs: define_loads()?, + }, + FileConfig { + name: "stores.isle".into(), + specs: define_stores()?, + }, + FileConfig { + name: "mov_wide.isle".into(), + specs: vec![define_mov_wide()?], + }, + FileConfig { + name: "extend.isle".into(), + specs: vec![define_extend()], + }, + FileConfig { + name: "conds.isle".into(), + specs: define_conds()?, + }, + FileConfig { + name: "fpu_move_imm.isle".into(), + specs: vec![define_fpu_move_imm()], + }, + FileConfig { + name: "fpu_cmp.isle".into(), + specs: vec![define_fpu_cmp()], + }, + FileConfig { + name: "fpu_rr.isle".into(), + specs: vec![define_fpu_rr()], + }, + FileConfig { + name: "fpu_round.isle".into(), + specs: vec![define_fpu_round()], + }, + FileConfig { + name: "fpu_rri.isle".into(), + specs: vec![define_fpu_rri()], + }, + FileConfig { + name: "fpu_rrimod.isle".into(), + specs: vec![define_fpu_rrimod()], + }, + FileConfig { + name: "fpu_rrr.isle".into(), + specs: vec![define_fpu_rrr()], + }, + FileConfig { + name: "mov_to_fpu.isle".into(), + specs: vec![define_mov_to_fpu()], + }, + FileConfig { + name: "int_to_fpu.isle".into(), + specs: vec![define_int_to_fpu()], + }, + FileConfig { + name: "fpu_to_int.isle".into(), + specs: vec![define_fpu_to_int()], + }, + FileConfig { + name: "mov_from_vec.isle".into(), + specs: vec![define_mov_from_vec()], + }, + FileConfig { + name: "vec_dup_imm.isle".into(), + specs: vec![define_vec_dup_imm()?], + }, + FileConfig { + name: "vec_rrr.isle".into(), + specs: vec![define_vec_rrr()], + }, + FileConfig { + name: "vec_misc.isle".into(), + specs: vec![define_vec_misc()], + }, + FileConfig { + name: "vec_lanes.isle".into(), + specs: vec![define_vec_lanes()], + }, + ]) +} + +// MInst.AluRRR specification configuration. +fn define_alu_rrr() -> SpecConfig { + let sizes = [OperandSize::Size64, OperandSize::Size32]; + let alu_ops = [ + ALUOp::Add, + ALUOp::Sub, + ALUOp::Orr, + ALUOp::OrrNot, + ALUOp::And, + ALUOp::AndNot, + ALUOp::Eor, + ALUOp::EorNot, + ALUOp::AddS, + ALUOp::SubS, + ALUOp::SMulH, + ALUOp::UMulH, + ALUOp::SDiv, + ALUOp::UDiv, + ALUOp::Extr, + ALUOp::Lsr, + ALUOp::Asr, + ALUOp::Lsl, + ALUOp::Adc, + // Flag ops not required yet: + // ALUOp::Sbc, + // ALUOp::AdcS, + // ALUOp::SbcS, + ]; + + spec_config! { + (AluRRR alu_op size rd rn rm) + { + enumerate (size, sizes); + enumerate (alu_op, alu_ops); + register (rd, write, gp, 4); + register (rn, read, gp, 5); + register (rm, read, gp, 6); + filter (is_alu_op_size_supported(alu_op, size)); + flags (); + instruction (); + } + } +} + +fn is_alu_op_size_supported(alu_op: ALUOp, size: OperandSize) -> bool { + match alu_op { + ALUOp::SMulH | ALUOp::UMulH => size == OperandSize::Size64, + _ => true, + } +} + +// MInst.AluRRRR specification configuration. +fn define_alu_rrrr() -> SpecConfig { + let sizes = [OperandSize::Size64, OperandSize::Size32]; + let alu3_ops = [ALUOp3::MAdd, ALUOp3::MSub, ALUOp3::UMAddL, ALUOp3::SMAddL]; + spec_config! { + (AluRRRR alu_op size rd rn rm ra) + { + enumerate (size, sizes); + enumerate (alu_op, alu3_ops); + register (rd, write, gp, 4); + register (rn, read, gp, 5); + register (rm, read, gp, 6); + register (ra, read, gp, 7); + filter (is_alu3_op_size_supported(alu_op, size)); + instruction (); + } + } +} + +fn is_alu3_op_size_supported(alu3_op: ALUOp3, size: OperandSize) -> bool { + match alu3_op { + ALUOp3::UMAddL | ALUOp3::SMAddL => size == OperandSize::Size32, + _ => true, + } +} + +// MInst.AluRRImm12 specification configuration. +fn define_alu_rr_imm12() -> Result { + // ALUOps supported by AluRRImm12. + let alu_ops = [ALUOp::Add, ALUOp::Sub, ALUOp::AddS, ALUOp::SubS]; + + // OperandSize + let sizes = [OperandSize::Size32, OperandSize::Size64]; + + // Imm12.shift12 + let shift12s = [false, true]; + + // Execution scope: define opcode template fields. + let mut scope = aarch64::state(); + let imm12_bits = Target::Var("bits".to_string()); + scope.global(imm12_bits.clone()); + + // Mappings + let mut mappings = flags_mappings(); + mappings.writes.insert( + aarch64::gpreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + mappings.reads.insert( + imm12_bits.clone(), + MappingBuilder::var("imm12").field("bits").build(), + ); + + Ok(SpecConfig { + term: "MInst.AluRRImm12".to_string(), + args: ["alu_op", "size", "rd", "rn", "imm12"] + .map(String::from) + .to_vec(), + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| { + Ok(Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Match(Match { + on: spec_var("alu_op".to_string()), + arms: alu_ops + .iter() + .map(|alu_op| { + Ok(Arm { + variant: format!("{alu_op:?}"), + args: Vec::new(), + body: Cases::Cases( + shift12s + .iter() + .map(|shift12| { + let template = alu_rr_imm12_template( + *alu_op, + *size, + writable_xreg(4), + xreg(5), + *shift12, + )?; + Ok(Case { + conds: vec![spec_eq_bool( + spec_field( + "shift12".to_string(), + spec_var("imm12".to_string()), + ), + *shift12, + )], + cases: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(template), + scope: scope.clone(), + mappings: mappings.clone(), + }), + }) + }) + .collect::>()?, + ), + }) + }) + .collect::>()?, + }), + }) + }) + .collect::>()?, + }), + }) +} + +fn alu_rr_imm12_template( + alu_op: ALUOp, + size: OperandSize, + rd: Writable, + rn: Reg, + shift12: bool, +) -> Result { + // Assemble a base instruction with a placeholder for the imm12 field. + let placeholder = Imm12 { bits: 0, shift12 }; + let base = Inst::AluRRImm12 { + alu_op, + size, + rd, + rn, + imm12: placeholder, + }; + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic immediate fields. + let imm = Bits { + segments: vec![Segment::Symbolic("bits".to_string(), 12)], + }; + let template = Bits::splice(&bits, &imm, 10)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let bits = assignment.get("bits").unwrap(); + let imm12 = Imm12 { + bits: (*bits).try_into().unwrap(), + shift12, + }; + Ok(Inst::AluRRImm12 { + alu_op, + size, + rd, + rn, + imm12, + }) + })?; + + Ok(template) +} + +// MInst.AluRRRShift specification configuration. +fn define_alu_rrr_shift() -> Result { + // ALUOps supported by AluRRImm12. + let alu_ops = [ + ALUOp::Add, + ALUOp::Sub, + ALUOp::Orr, + ALUOp::And, + ALUOp::Eor, + ALUOp::OrrNot, + ALUOp::EorNot, + ALUOp::AndNot, + ALUOp::Extr, + // Flags: + // ALUOp::AddS, + // ALUOp::SubS, + // ALUOp::AndS, + ]; + + // OperandSize + let sizes = [OperandSize::Size32, OperandSize::Size64]; + + // ShiftOp + // + // For the genuinely shifted-register ops, LSL/LSR/ASR are all valid for + // both operand sizes. ShiftOp::ROR is a defined variant but not a valid + // opcode here, so it is omitted. + let default_shiftops = [ShiftOp::LSL, ShiftOp::LSR, ShiftOp::ASR]; + + // `Extr` reuses the `AluRRRShift` shape but is not actually a + // shifted-register instruction: the low bit of the shift-op field encodes + // the architectural `N` bit, which AArch64 requires to equal `sf` (the + // operand-size bit), otherwise the encoding is UNDEFINED. So `Extr` only + // assembles to a real instruction for the shift-ops whose encoding makes + // `N == sf`: LSL (0b00, N=0) for the 32-bit size and LSR (0b01, N=1) for + // the 64-bit size. This matches `a64_extr_imm`, the only producer of + // `AluRRRShift { Extr, .. }` in lowering. See `alu_rrr_shift_sizes` for the + // size restriction that pairs each of these shift-ops with its one valid + // operand size. + let extr_shiftops = [ShiftOp::LSL, ShiftOp::LSR]; + + Ok(SpecConfig { + term: "MInst.AluRRRShift".to_string(), + args: ["alu_op", "size", "rd", "rn", "rm", "shiftop"] + .map(String::from) + .to_vec(), + cases: Cases::Match(Match { + on: spec_var("alu_op".to_string()), + arms: alu_ops + .iter() + .map(|alu_op| { + let shiftops: &[ShiftOp] = if matches!(*alu_op, ALUOp::Extr) { + &extr_shiftops + } else { + &default_shiftops + }; + Ok(Arm { + variant: format!("{alu_op:?}"), + args: Vec::new(), + + // Shift operation cases. + // + // Note that the `ShiftOpAndAmt` model actually uses + // `ALUOp` to represent the shift operation. ISLE does + // not actually materialize the ShiftOp type itself, and + // without support for ghost types, we cannot represent + // it. Therefore, co-opting ALUOp for the purpose is + // the best we can do. + body: Cases::Match(Match { + on: spec_field("op".to_string(), spec_var("shiftop".to_string())), + arms: shiftops + .iter() + .map(|shiftop| { + // Map shift operation to the correspondong ALUOp. + let alu_shift_op = alu_op_from_shiftop(*shiftop); + Ok(Arm { + variant: format!("{alu_shift_op:?}"), + args: Vec::new(), + body: Cases::Cases( + alu_rrr_shift_sizes(*alu_op, *shiftop, &sizes) + .into_iter() + .map(|size| { + alu_rrr_shift_size_case(*alu_op, size, *shiftop) + }) + .collect::>()?, + ), + }) + }) + .collect::>()?, + }), + }) + }) + .collect::>()?, + }), + }) +} + +fn alu_rrr_shift_size_case(alu_op: ALUOp, size: OperandSize, op: ShiftOp) -> Result { + // Shift amount field depends on operand size. + let amt_width = match size { + OperandSize::Size32 => 5, + OperandSize::Size64 => 6, + }; + let amt_var = format!("amt{}", amt_width); + + // Setup scope with shift amount variable. + let amt_target = Target::Var(amt_var.clone()); + let mut scope = aarch64::state(); + scope.global(amt_target.clone()); + + // Expressions for the shift amount. + // + // The model of the shift amount is an 8 bit value, but the instruction + // representations only allow 5 or 6 bits (depending on operand size). We + // extract the shift bits from the operand, and require that the higher bits + // are zero. + static FULL_AMT_WIDTH: usize = 8; + let full_amt_expr = spec_field("amt".to_string(), spec_var("shiftop".to_string())); + let amt_expr = spec_extract(amt_width - 1, 0, full_amt_expr.clone()); + let amt_overflow_expr = spec_extract(FULL_AMT_WIDTH - 1, amt_width, full_amt_expr.clone()); + let amt_overflow_width = FULL_AMT_WIDTH - amt_width; + let no_amt_overflow = spec_eq( + amt_overflow_expr, + spec_const_bit_vector(0, amt_overflow_width), + ); + + // Mappings + let mut mappings = flags_mappings(); + mappings.writes.insert( + aarch64::gpreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + mappings.reads.insert( + aarch64::gpreg(6), + Mapping::require(spec_var("rm".to_string())), + ); + mappings + .reads + .insert(amt_target.clone(), Mapping::require(amt_expr.clone())); + + // Opcode template + // + // Assemble a base instruction with a placeholder for the shift amount. + let placeholder = ShiftOpShiftImm::maybe_from_shift(0).unwrap(); + let rd = writable_xreg(4); + let rn = xreg(5); + let rm = xreg(6); + let shiftop = ShiftOpAndAmt::new(op, placeholder); + let base = Inst::AluRRRShift { + alu_op, + size, + rd, + rn, + rm, + shiftop, + }; + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic shift amount. + // + // The shift amount is 6 bits in the 64-bit case, and 5 bits in the 32-bit + // case. Note that in the 32-bit case, the instruction is explicitly + // undefined when bit 5 is 1. Therefore, we must ensure that the symbolic + // field variable is only 5 bits. + let amt = Bits { + segments: vec![Segment::Symbolic(amt_var.to_string(), amt_width)], + }; + let template = Bits::splice(&bits, &amt, 10)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let amt = assignment.get(&amt_var).unwrap(); + let shift = ShiftOpShiftImm::maybe_from_shift((*amt).into()).unwrap(); + let shiftop = ShiftOpAndAmt::new(op, shift); + Ok(Inst::AluRRRShift { + alu_op, + size, + rd, + rn, + rm, + shiftop, + }) + })?; + + Ok(Case { + conds: vec![ + spec_discriminator(format!("{size:?}"), spec_var("size".to_string())), + no_amt_overflow, + ], + cases: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(template), + scope: scope.clone(), + mappings: mappings.clone(), + }), + }) +} + +/// Operand sizes for which the `(alu_op, op)` pairing assembles to a real +/// instruction, in the order they should appear in the generated spec. +fn alu_rrr_shift_sizes(alu_op: ALUOp, op: ShiftOp, sizes: &[OperandSize]) -> Vec { + match (alu_op, op) { + // `Extr` requires the architectural `N` bit (the low bit of the + // shift-op encoding) to equal `sf`, so each shift-op is valid for + // exactly one operand size: LSL (N=0) -> 32-bit, LSR (N=1) -> 64-bit. + // Any other pairing emits an UNDEFINED encoding (`N != sf`). + (ALUOp::Extr, ShiftOp::LSL) => vec![OperandSize::Size32], + (ALUOp::Extr, ShiftOp::LSR) => vec![OperandSize::Size64], + _ => sizes.iter().rev().copied().collect(), + } +} + +fn alu_op_from_shiftop(op: ShiftOp) -> ALUOp { + match op { + ShiftOp::LSL => ALUOp::Lsl, + ShiftOp::LSR => ALUOp::Lsr, + ShiftOp::ASR => ALUOp::Asr, + ShiftOp::ROR => ALUOp::Extr, + } +} + +// MInst.AluRRRExtend specification configuration. +fn define_alu_rrr_extend() -> SpecConfig { + // ALUOps supported by AluRRRExtend. + let alu_ops = [ALUOp::Add, ALUOp::Sub, ALUOp::AddS, ALUOp::SubS]; + + // OperandSize + let sizes = [OperandSize::Size64, OperandSize::Size32]; + + // ExtendOp + let extendops = [ + ExtendOp::UXTB, + ExtendOp::UXTH, + ExtendOp::UXTW, + ExtendOp::UXTX, + ExtendOp::SXTB, + ExtendOp::SXTH, + ExtendOp::SXTW, + ExtendOp::SXTX, + ]; + + spec_config! { + (AluRRRExtend alu_op size rd rn rm extendop) + { + enumerate (size, sizes); + enumerate (alu_op, alu_ops); + register (rd, write, gp, 4); + register (rn, read, gp, 5); + register (rm, read, gp, 6); + enumerate (extendop, extendops); + flags (); + instruction (); + } + } +} + +// MInst.BitRR specification configuration. +fn define_bit_rr() -> SpecConfig { + // BitRR + let bit_ops = [ + BitOp::Cls, + BitOp::RBit, + BitOp::Clz, + // -------------- + // BitOp::Rev16, + // BitOp::Rev32, + // BitOp::Rev64, + ]; + + // OperandSize + let sizes = [OperandSize::Size64, OperandSize::Size32]; + + spec_config! { + (BitRR op size rd rn) + { + enumerate (size, sizes); + enumerate (op, bit_ops); + register (rd, write, gp, 4); + register (rn, read, gp64, 5); + instruction (); + } + } +} + +// MInst.MovWide specification configuration. +fn define_mov_wide() -> Result { + // MovWideOps + let mov_wide_ops = [MoveWideOp::MovZ, MoveWideOp::MovN]; + + // OperandSize + let sizes = [OperandSize::Size32, OperandSize::Size64]; + + // Execution scope: define opcode template fields. + let mut scope = aarch64::state(); + let mov_wide_const_bits = Target::Var("bits".to_string()); + scope.global(mov_wide_const_bits.clone()); + + // Mappings + let mut mappings = Mappings::default(); + mappings.writes.insert( + aarch64::gpreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + mov_wide_const_bits.clone(), + MappingBuilder::var("imm").field("bits").build(), + ); + + Ok(SpecConfig { + term: "MInst.MovWide".to_string(), + args: ["op", "rd", "imm", "size"].map(String::from).to_vec(), + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| { + let max_shift = size.bits() / 16; + Ok(Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Match(Match { + on: spec_var("op".to_string()), + arms: mov_wide_ops + .iter() + .map(|mov_wide_op| { + Ok(Arm { + variant: format!("{mov_wide_op:?}"), + args: Vec::new(), + body: Cases::Cases( + (0..max_shift) + .map(|shift| { + let template = mov_wide_template( + *mov_wide_op, + writable_xreg(4), + shift, + *size, + )?; + Ok(Case { + conds: vec![spec_eq( + spec_field( + "shift".to_string(), + spec_var("imm".to_string()), + ), + spec_const_bit_vector(shift.into(), 2), + )], + cases: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(template), + scope: scope.clone(), + mappings: mappings.clone(), + }), + }) + }) + .collect::>()?, + ), + }) + }) + .collect::>()?, + }), + }) + }) + .collect::>()?, + }), + }) +} + +fn mov_wide_template( + op: MoveWideOp, + rd: Writable, + shift: u8, + size: OperandSize, +) -> Result { + // Assemble a base instruction with a placeholder for the immediate bits field. + let placeholder = MoveWideConst { bits: 0, shift }; + let base = Inst::MovWide { + op, + rd, + imm: placeholder, + size, + }; + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic immediate fields. + let imm = Bits { + segments: vec![Segment::Symbolic("bits".to_string(), 16)], + }; + let template = Bits::splice(&bits, &imm, 5)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let bits = assignment.get("bits").unwrap(); + let imm = MoveWideConst { + bits: (*bits).try_into().unwrap(), + shift, + }; + Ok(Inst::MovWide { op, rd, imm, size }) + })?; + + Ok(template) +} + +fn define_extend() -> SpecConfig { + // Extend + let signed = [false, true]; + let bits = [8u8, 16u8, 32u8, 64u8]; + + let mut mappings = Mappings::default(); + mappings.writes.insert( + aarch64::gpreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_as_bit_vector_width(spec_var("rn".to_string()), 64)), + ); + + SpecConfig { + // Spec signature. + term: "MInst.Extend".to_string(), + args: ["rd", "rn", "signed", "from_bits", "to_bits"] + .map(String::from) + .to_vec(), + cases: Cases::Cases( + bits.iter() + .cartesian_product(&bits) + .filter(|(from_bits, to_bits)| from_bits <= to_bits && **from_bits < 64) + .cartesian_product(&signed) + .map(|((from_bits, to_bits), signed)| Case { + conds: vec![ + spec_eq_bool(spec_var("signed".to_string()), *signed), + spec_eq( + spec_var("from_bits".to_string()), + spec_const_bit_vector((*from_bits).into(), 8), + ), + spec_eq( + spec_var("to_bits".to_string()), + spec_const_bit_vector((*to_bits).into(), 8), + ), + ], + cases: Cases::Instruction(InstConfig { + // Instruction to generate specification from. + opcodes: Opcodes::Instruction(Inst::Extend { + rd: writable_xreg(4), + rn: xreg(5), + signed: *signed, + from_bits: *from_bits, + to_bits: *to_bits, + }), + scope: aarch64::state(), + mappings: mappings.clone(), + }), + }) + .collect(), + ), + } +} + +fn define_loads() -> Result> { + // Destination register for general-purpose loads. + let dst = writable_xreg(4); + let rd = spec_var("rd".to_string()); + + // ULoad8 + let uload8 = define_load("MInst.ULoad8", 8, dst, &rd, |rd, mem, flags| Inst::ULoad8 { + rd, + mem, + flags, + })?; + + // SLoad8 + let sload8 = define_load("MInst.SLoad8", 8, dst, &rd, |rd, mem, flags| Inst::SLoad8 { + rd, + mem, + flags, + })?; + + // ULoad16 + let uload16 = define_load("MInst.ULoad16", 16, dst, &rd, |rd, mem, flags| { + Inst::ULoad16 { rd, mem, flags } + })?; + + // SLoad16 + let sload16 = define_load("MInst.SLoad16", 16, dst, &rd, |rd, mem, flags| { + Inst::SLoad16 { rd, mem, flags } + })?; + + // ULoad32 + let uload32 = define_load("MInst.ULoad32", 32, dst, &rd, |rd, mem, flags| { + Inst::ULoad32 { rd, mem, flags } + })?; + + // SLoad32 + let sload32 = define_load("MInst.SLoad32", 32, dst, &rd, |rd, mem, flags| { + Inst::SLoad32 { rd, mem, flags } + })?; + + // ULoad64 + let uload64 = define_load("MInst.ULoad64", 64, dst, &rd, |rd, mem, flags| { + Inst::ULoad64 { rd, mem, flags } + })?; + + // Destination register for floating-point loads. + let dst = writable_vreg(4); + let rd = spec_fp_reg("rd"); + + // FpuLoad32 + let fpu_load32 = define_load("MInst.FpuLoad32", 32, dst, &rd, |rd, mem, flags| { + Inst::FpuLoad32 { rd, mem, flags } + })?; + + // FpuLoad64 + let fpu_load64 = define_load("MInst.FpuLoad64", 64, dst, &rd, |rd, mem, flags| { + Inst::FpuLoad64 { rd, mem, flags } + })?; + + Ok(vec![ + uload8, sload8, uload16, sload16, uload32, sload32, uload64, fpu_load32, fpu_load64, + ]) +} + +fn define_load( + term: &str, + size_bits: usize, + dst: Writable, + rd: &SpecExpr, + inst: F, +) -> Result +where + F: Fn(Writable, AMode, MemFlagsData) -> Inst, +{ + // Mappings. + let mut mappings = Mappings::default(); + + // Destination register. + mappings + .writes + .insert(reg_target(dst.to_reg())?, Mapping::require(rd.clone())); + + // ISA load state mapped to read effect. + let read_effect = ReadEffect::new(); + static ISA_LOAD: &str = "isa_load"; + static LOADED_VALUE: &str = "loaded_value"; + mappings.writes.insert( + read_effect.active, + MappingBuilder::state(ISA_LOAD).field("active").build(), + ); + mappings.writes.insert( + read_effect.addr, + MappingBuilder::state(ISA_LOAD).field("addr").build(), + ); + mappings.writes.insert( + read_effect.size_bits, + MappingBuilder::state(ISA_LOAD).field("size_bits").build(), + ); + mappings.reads.insert( + read_effect.value, + Mapping::require(spec_binary( + SpecOp::ConvTo, + spec_const_int(size_bits.try_into().unwrap()), + spec_var(LOADED_VALUE.to_string()), + )), + ); + + // Enumerate AModes. + let arms = amode_cases(&mappings, |mem, flags| inst(dst, mem, flags))?; + + Ok(SpecConfig { + term: term.to_string(), + args: ["rd", "mem", "flags"].map(String::from).to_vec(), + cases: Cases::Match(Match { + on: spec_var("mem".to_string()), + arms, + }), + }) +} + +fn define_stores() -> Result> { + // Source register for general-purpose loads. + let src = xreg(4); + let rd = spec_as_bit_vector_width(spec_var("rd".to_string()), 64); + + // Store8 + let store8 = define_store("MInst.Store8", 8, src, &rd, |rd, mem, flags| Inst::Store8 { + rd, + mem, + flags, + })?; + + // Store16 + let store16 = define_store("MInst.Store16", 16, src, &rd, |rd, mem, flags| { + Inst::Store16 { rd, mem, flags } + })?; + + // Store32 + let store32 = define_store("MInst.Store32", 32, src, &rd, |rd, mem, flags| { + Inst::Store32 { rd, mem, flags } + })?; + + // Store64 + let store64 = define_store("MInst.Store64", 64, src, &rd, |rd, mem, flags| { + Inst::Store64 { rd, mem, flags } + })?; + + // Source register for floating-point stores. + let src = vreg(4); + let rd = spec_fp_reg("rd"); + + // FpuStore32 + let fpu_store32 = define_store("MInst.FpuStore32", 32, src, &rd, |rd, mem, flags| { + Inst::FpuStore32 { rd, mem, flags } + })?; + + // FpuStore64 + let fpu_store64 = define_store("MInst.FpuStore64", 64, src, &rd, |rd, mem, flags| { + Inst::FpuStore64 { rd, mem, flags } + })?; + + Ok(vec![ + store8, + store16, + store32, + store64, + fpu_store32, + fpu_store64, + ]) +} + +fn define_store( + term: &str, + size_bits: usize, + src: Reg, + rd: &SpecExpr, + inst: F, +) -> Result +where + F: Fn(Reg, AMode, MemFlagsData) -> Inst, +{ + // Mappings. + let mut mappings = Mappings::default(); + + // Source register. + mappings + .reads + .insert(reg_target(src)?, Mapping::require(rd.clone())); + + // ISA store state mapped to memory set effect. + let set_effect = SetEffect::new(); + static ISA_STORE: &str = "isa_store"; + mappings.writes.insert( + set_effect.active, + MappingBuilder::state(ISA_STORE).field("active").build(), + ); + mappings.writes.insert( + set_effect.addr, + MappingBuilder::state(ISA_STORE).field("addr").build(), + ); + mappings.writes.insert( + set_effect.size_bits, + MappingBuilder::state(ISA_STORE).field("size_bits").build(), + ); + mappings.writes.insert( + set_effect.value, + MappingBuilder::new(spec_binary( + SpecOp::ConvTo, + spec_const_int(size_bits.try_into().unwrap()), + spec_field("value".to_string(), spec_var(ISA_STORE.to_string())), + )) + .modifies(ISA_STORE) + .build(), + ); + + // Enumerate AModes. + let arms = amode_cases(&mappings, |mem, flags| inst(src, mem, flags))?; + + Ok(SpecConfig { + term: term.to_string(), + args: ["rd", "mem", "flags"].map(String::from).to_vec(), + cases: Cases::Match(Match { + on: spec_var("mem".to_string()), + arms, + }), + }) +} + +fn amode_cases(mappings: &Mappings, inst: F) -> Result> +where + F: Fn(AMode, MemFlagsData) -> Inst, +{ + // RegReg + let mut reg_reg_mappings = mappings.clone(); + reg_reg_mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + reg_reg_mappings.reads.insert( + aarch64::gpreg(6), + Mapping::require(spec_var("rm".to_string())), + ); + + let reg_reg = Arm { + variant: "RegReg".to_string(), + args: ["rn", "rm"].map(String::from).to_vec(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(inst( + AMode::RegReg { + rn: xreg(5), + rm: xreg(6), + }, + MemFlagsData::new(), + )), + scope: aarch64::state(), + mappings: reg_reg_mappings, + }), + }; + + // RegScaled + let mut reg_scaled_mappings = mappings.clone(); + reg_scaled_mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + reg_scaled_mappings.reads.insert( + aarch64::gpreg(6), + Mapping::require(spec_var("rm".to_string())), + ); + + let reg_scaled = Arm { + variant: "RegScaled".to_string(), + args: ["rn", "rm"].map(String::from).to_vec(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(inst( + AMode::RegScaled { + rn: xreg(5), + rm: xreg(6), + }, + MemFlagsData::new(), + )), + scope: aarch64::state(), + mappings: reg_scaled_mappings, + }), + }; + + // RegScaledExtended + let extendops = [ + // Not supported by assembler: UXTB, UXTH, UXTX, SXTB, SXTH + ExtendOp::UXTW, + ExtendOp::SXTW, + ExtendOp::SXTX, + ]; + let mut reg_scaled_extended_mappings = mappings.clone(); + reg_scaled_extended_mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + reg_scaled_extended_mappings.reads.insert( + aarch64::gpreg(6), + Mapping::require(spec_var("rm".to_string())), + ); + + let reg_scaled_extended = Arm { + variant: "RegScaledExtended".to_string(), + args: ["rn", "rm", "extendop"].map(String::from).to_vec(), + body: Cases::Match(Match { + on: spec_var("extendop".to_string()), + arms: extendops + .into_iter() + .map(|extendop| Arm { + variant: format!("{extendop:?}"), + args: Vec::new(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(inst( + AMode::RegScaledExtended { + rn: xreg(5), + rm: xreg(6), + extendop, + }, + MemFlagsData::new(), + )), + scope: aarch64::state(), + mappings: reg_scaled_extended_mappings.clone(), + }), + }) + .collect(), + }), + }; + + // RegExtended + let mut reg_extended_mappings = mappings.clone(); + reg_extended_mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + reg_extended_mappings.reads.insert( + aarch64::gpreg(6), + Mapping::require(spec_var("rm".to_string())), + ); + + let reg_extended = Arm { + variant: "RegExtended".to_string(), + args: ["rn", "rm", "extendop"].map(String::from).to_vec(), + body: Cases::Match(Match { + on: spec_var("extendop".to_string()), + arms: extendops + .into_iter() + .map(|extendop| Arm { + variant: format!("{extendop:?}"), + args: Vec::new(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(inst( + AMode::RegExtended { + rn: xreg(5), + rm: xreg(6), + extendop, + }, + MemFlagsData::new(), + )), + scope: aarch64::state(), + mappings: reg_extended_mappings.clone(), + }), + }) + .collect(), + }), + }; + + // Unscaled + let mut unscaled_scope = aarch64::state(); + let simm9 = Target::Var("simm9".to_string()); + unscaled_scope.global(simm9.clone()); + + let mut unscaled_mappings = mappings.clone(); + unscaled_mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + unscaled_mappings + .reads + .insert(simm9, Mapping::require(spec_var("simm9".to_string()))); + + let unscaled_template = + amode_unscaled_template(xreg(5), |amode| inst(amode, MemFlagsData::new()))?; + + let unscaled = Arm { + variant: "Unscaled".to_string(), + args: ["rn", "simm9"].map(String::from).to_vec(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(unscaled_template), + scope: unscaled_scope, + mappings: unscaled_mappings.clone(), + }), + }; + + // UnsignedOffset + let mut unsigned_offset_scope = aarch64::state(); + let uimm12 = Target::Var("uimm12".to_string()); + unsigned_offset_scope.global(uimm12.clone()); + + let mut unsigned_offset_mappings = mappings.clone(); + unsigned_offset_mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + unsigned_offset_mappings + .reads + .insert(uimm12, Mapping::require(spec_var("uimm12".to_string()))); + + let unsigned_offset_template = + amode_unsigned_offset_template(xreg(5), |amode| inst(amode, MemFlagsData::new()))?; + + let unsigned_offset = Arm { + variant: "UnsignedOffset".to_string(), + args: ["rn", "uimm12"].map(String::from).to_vec(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(unsigned_offset_template), + scope: unsigned_offset_scope, + mappings: unsigned_offset_mappings, + }), + }; + + Ok(vec![ + reg_reg, + reg_scaled, + reg_scaled_extended, + reg_extended, + unscaled, + unsigned_offset, + ]) +} + +fn amode_unscaled_template(rn: Reg, inst: F) -> Result +where + F: Fn(AMode) -> Inst, +{ + // Assemble a base instruction with a placeholder for the immediate bits field. + let placeholder = SImm9::maybe_from_i64(0).unwrap(); + let base = inst(AMode::Unscaled { + rn, + simm9: placeholder, + }); + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic immediate fields. + let imm = Bits { + segments: vec![Segment::Symbolic("simm9".to_string(), 9)], + }; + let template = Bits::splice(&bits, &imm, 12)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let bits = assignment.get("simm9").unwrap(); + let imm = SImm9 { + value: (*bits).try_into().unwrap(), + }; + Ok(inst(AMode::Unscaled { rn, simm9: imm })) + })?; + + Ok(template) +} + +fn amode_unsigned_offset_template(rn: Reg, inst: F) -> Result +where + F: Fn(AMode) -> Inst, +{ + // Assemble a base instruction with a placeholder for the immediate bits field. + let placeholder = UImm12Scaled::zero(I8); + let base = inst(AMode::UnsignedOffset { + rn, + uimm12: placeholder, + }); + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic immediate fields. + let imm = Bits { + segments: vec![Segment::Symbolic("uimm12".to_string(), 12)], + }; + let template = Bits::splice(&bits, &imm, 10)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let bits = assignment.get("uimm12").unwrap(); + let uimm12 = UImm12Scaled::maybe_from_i64((*bits).into(), I8).unwrap(); + Ok(inst(AMode::UnsignedOffset { rn, uimm12 })) + })?; + + Ok(template) +} + +fn define_conds() -> Result> { + // CSel + let csel = define_csel("MInst.CSel", |rd, cond, rn, rm| Inst::CSel { + rd, + cond, + rn, + rm, + }); + + // CSNeg + let csneg = define_csel("MInst.CSNeg", |rd, cond, rn, rm| Inst::CSNeg { + rd, + cond, + rn, + rm, + }); + + // CSet + let cset = define_cset("MInst.CSet", |rd, cond| Inst::CSet { rd, cond }); + + // CSetm + let csetm = define_cset("MInst.CSetm", |rd, cond| Inst::CSetm { rd, cond }); + + // CCmp + let ccmp = define_ccmp()?; + + // CCmpImm + let ccmp_imm = define_ccmp_imm()?; + + Ok(vec![csel, csneg, cset, csetm, ccmp, ccmp_imm]) +} + +fn define_csel(term: &str, inst: F) -> SpecConfig +where + F: Fn(Writable, Cond, Reg, Reg) -> Inst, +{ + // Flags and register mappings. + let mut mappings = flags_mappings(); + mappings.writes.insert( + aarch64::gpreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + mappings.reads.insert( + aarch64::gpreg(6), + Mapping::require(spec_var("rm".to_string())), + ); + + SpecConfig { + term: term.to_string(), + args: ["rd", "cond", "rn", "rm"].map(String::from).to_vec(), + + cases: Cases::Match(Match { + on: spec_var("cond".to_string()), + arms: conds() + .iter() + .rev() + .map(|cond| Arm { + variant: format!("{cond:?}"), + args: Vec::new(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(inst( + writable_xreg(4), + *cond, + xreg(5), + xreg(6), + )), + scope: aarch64::state(), + mappings: mappings.clone(), + }), + }) + .collect(), + }), + } +} + +fn define_cset(term: &str, inst: F) -> SpecConfig +where + F: Fn(Writable, Cond) -> Inst, +{ + // Flags and register mappings. + let mut mappings = flags_mappings(); + mappings.writes.insert( + aarch64::gpreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + + SpecConfig { + term: term.to_string(), + args: ["rd", "cond"].map(String::from).to_vec(), + + cases: Cases::Match(Match { + on: spec_var("cond".to_string()), + arms: conds() + .iter() + .rev() + .map(|cond| Arm { + variant: format!("{cond:?}"), + args: Vec::new(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(inst(writable_xreg(4), *cond)), + scope: aarch64::state(), + mappings: mappings.clone(), + }), + }) + .collect(), + }), + } +} + +fn define_ccmp() -> Result { + // OperandSize + let sizes = [OperandSize::Size32, OperandSize::Size64]; + + // Execution scope: define opcode template fields. + let mut scope = aarch64::state(); + for flag in &["n", "z", "c", "v"] { + scope.global(Target::Var(flag.to_string())); + } + + // Flags and register mappings. + let mut mappings = flags_mappings(); + mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + mappings.reads.insert( + aarch64::gpreg(6), + Mapping::require(spec_var("rm".to_string())), + ); + for flag in &["n", "z", "c", "v"] { + mappings.reads.insert( + Target::Var(flag.to_string()), + MappingBuilder::var("nzcv") + .field(&flag.to_uppercase()) + .build(), + ); + } + + Ok(SpecConfig { + term: "MInst.CCmp".to_string(), + args: ["size", "rn", "rm", "nzcv", "cond"] + .map(String::from) + .to_vec(), + + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| { + Ok(Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Match(Match { + on: spec_var("cond".to_string()), + arms: conds() + .iter() + .rev() + .map(|cond| { + let template = ccmp_template(*size, xreg(5), xreg(6), *cond)?; + Ok(Arm { + variant: format!("{cond:?}"), + args: Vec::new(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(template), + scope: scope.clone(), + mappings: mappings.clone(), + }), + }) + }) + .collect::>()?, + }), + }) + }) + .collect::>()?, + }), + }) +} + +fn ccmp_template(size: OperandSize, rn: Reg, rm: Reg, cond: Cond) -> Result { + // Assemble a base instruction with a placeholder for the NZCV field. + let placeholder = NZCV::new(false, false, false, false); + let base = Inst::CCmp { + size, + rn, + rm, + nzcv: placeholder, + cond, + }; + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic immediate fields. + let nzcv = Bits { + segments: vec![ + Segment::Symbolic("v".to_string(), 1), + Segment::Symbolic("c".to_string(), 1), + Segment::Symbolic("z".to_string(), 1), + Segment::Symbolic("n".to_string(), 1), + ], + }; + let template = Bits::splice(&bits, &nzcv, 0)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let nzcv = NZCV::new( + assignment["n"] != 0, + assignment["z"] != 0, + assignment["c"] != 0, + assignment["v"] != 0, + ); + Ok(Inst::CCmp { + size, + rn, + rm, + nzcv, + cond, + }) + })?; + + Ok(template) +} + +fn define_ccmp_imm() -> Result { + // OperandSize + let sizes = [OperandSize::Size32, OperandSize::Size64]; + + // Execution scope: define opcode template fields. + let mut scope = aarch64::state(); + let imm_var = Target::Var("imm".to_string()); + scope.global(imm_var.clone()); + for flag in &["n", "z", "c", "v"] { + scope.global(Target::Var(flag.to_string())); + } + + // Flags and register mappings. + let mut mappings = flags_mappings(); + mappings.reads.insert( + aarch64::gpreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + mappings + .reads + .insert(imm_var, Mapping::require(spec_var("imm".to_string()))); + for flag in &["n", "z", "c", "v"] { + mappings.reads.insert( + Target::Var(flag.to_string()), + MappingBuilder::var("nzcv") + .field(&flag.to_uppercase()) + .build(), + ); + } + + Ok(SpecConfig { + term: "MInst.CCmpImm".to_string(), + args: ["size", "rn", "imm", "nzcv", "cond"] + .map(String::from) + .to_vec(), + + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| { + Ok(Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Match(Match { + on: spec_var("cond".to_string()), + arms: conds() + .iter() + .rev() + .map(|cond| { + let template = ccmp_imm_template(*size, xreg(5), *cond)?; + Ok(Arm { + variant: format!("{cond:?}"), + args: Vec::new(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(template), + scope: scope.clone(), + mappings: mappings.clone(), + }), + }) + }) + .collect::>()?, + }), + }) + }) + .collect::>()?, + }), + }) +} + +fn ccmp_imm_template(size: OperandSize, rn: Reg, cond: Cond) -> Result { + // Assemble a base instruction with a placeholder for the immediate and NZCV fields. + let imm_placeholder = UImm5::maybe_from_u8(0).unwrap(); + let nzcv_placeholder = NZCV::new(false, false, false, false); + let base = Inst::CCmpImm { + size, + rn, + imm: imm_placeholder, + nzcv: nzcv_placeholder, + cond, + }; + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic immediate fields. + let nzcv = Bits { + segments: vec![ + Segment::Symbolic("v".to_string(), 1), + Segment::Symbolic("c".to_string(), 1), + Segment::Symbolic("z".to_string(), 1), + Segment::Symbolic("n".to_string(), 1), + ], + }; + let template = Bits::splice(&bits, &nzcv, 0)?; + let imm = Bits { + segments: vec![Segment::Symbolic("imm".to_string(), 5)], + }; + let template = Bits::splice(&template, &imm, 16)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let imm = UImm5::maybe_from_u8(assignment["imm"].try_into()?).unwrap(); + let nzcv = NZCV::new( + assignment["n"] != 0, + assignment["z"] != 0, + assignment["c"] != 0, + assignment["v"] != 0, + ); + Ok(Inst::CCmpImm { + size, + rn, + imm, + nzcv, + cond, + }) + })?; + + Ok(template) +} + +/// All condition codes. +fn conds() -> Vec { + vec![ + Cond::Eq, + Cond::Ne, + Cond::Hs, + Cond::Lo, + Cond::Mi, + Cond::Pl, + Cond::Vs, + Cond::Vc, + Cond::Hi, + Cond::Ls, + Cond::Ge, + Cond::Lt, + Cond::Gt, + Cond::Le, + ] +} + +// MInst.FpuRRR specification configuration. +fn define_fpu_rrr() -> SpecConfig { + // FPUOp2 + let fpu_op2s = [ + FPUOp2::Add, + FPUOp2::Sub, + FPUOp2::Mul, + FPUOp2::Div, + FPUOp2::Min, + FPUOp2::Max, + ]; + + // ScalarSize + let sizes = [ScalarSize::Size64, ScalarSize::Size32]; + + spec_config! { + (FpuRRR fpu_op size rd rn rm) + { + enumerate (size, sizes); + enumerate (fpu_op, fpu_op2s); + register (rd, write, fp, 4); + register (rn, read, fp, 5); + register (rm, read, fp, 6); + fpcr (); + instruction (); + } + } +} + +// MInst.FpuRR specification configuration. +fn define_fpu_rr() -> SpecConfig { + // FPUOp1 + let fpu_op1s = [ + FPUOp1::Neg, + FPUOp1::Abs, + FPUOp1::Sqrt, + FPUOp1::Cvt64To32, + FPUOp1::Cvt32To64, + ]; + + // ScalarSize + let sizes = [ScalarSize::Size64, ScalarSize::Size32]; + + spec_config! { + (FpuRR fpu_op size rd rn) + { + enumerate (size, sizes); + enumerate (fpu_op, fpu_op1s); + register (rd, write, fp, 4); + register (rn, read, fp, 5); + filter (is_fpu_op1_size_supported(fpu_op, size)); + fpcr (); + instruction (); + } + } +} + +fn is_fpu_op1_size_supported(fpu_op1: FPUOp1, size: ScalarSize) -> bool { + match fpu_op1 { + FPUOp1::Cvt64To32 => size == ScalarSize::Size64, + FPUOp1::Cvt32To64 => size == ScalarSize::Size32, + _ => true, + } +} + +fn define_fpu_move_imm() -> SpecConfig { + // ScalarSize + let sizes = [ScalarSize::Size32, ScalarSize::Size64]; + + // Execution scope: define opcode template fields. + let mut scope = aarch64::state(); + let imm_bits = Target::Var("bits".to_string()); + scope.global(imm_bits.clone()); + + // Mappings + let mut mappings = flags_mappings(); + mappings.writes.insert( + aarch64::vreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + imm_bits.clone(), + MappingBuilder::var("imm").field("imm").build(), + ); + + SpecConfig { + term: "MInst.FpuMoveFPImm".to_string(), + args: ["rd", "imm", "size"].map(String::from).to_vec(), + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Instruction({ + InstConfig { + opcodes: Opcodes::Template( + fpu_move_imm_template(*size, writable_vreg(4)).unwrap(), + ), + scope: scope.clone(), + mappings: mappings.clone(), + } + }), + }) + .collect(), + }), + } +} + +fn fpu_move_imm_template(size: ScalarSize, rd: Writable) -> Result { + // Assemble a base instruction with a placeholder for the imm12 field. + let placeholder = ASIMDFPModImm { imm: 0, size }; + let base = Inst::FpuMoveFPImm { + rd, + imm: placeholder, + size, + }; + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + // Splice in symbolic immediate fields. + let imm = Bits { + segments: vec![Segment::Symbolic("bits".to_string(), 8)], + }; + let template = Bits::splice(&bits, &imm, 13)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let bits = assignment.get("bits").unwrap(); + let imm = ASIMDFPModImm { + imm: (*bits).try_into().unwrap(), + size, + }; + Ok(Inst::FpuMoveFPImm { rd, imm, size }) + })?; + + Ok(template) +} + +fn define_fpu_cmp() -> SpecConfig { + // ScalarSize + let sizes = [ScalarSize::Size64, ScalarSize::Size32]; + + spec_config! { + (FpuCmp size rn rm) + { + enumerate (size, sizes); + register (rn, read, fp, 4); + register (rm, read, fp, 5); + fpcr (); + flags (); + instruction (); + } + } +} + +// MInst.MovToFpu specification configuration. +fn define_mov_to_fpu() -> SpecConfig { + // ScalarSize + let sizes = [ScalarSize::Size64, ScalarSize::Size32, ScalarSize::Size16]; + + spec_config! { + (MovToFpu rd rn size) + { + enumerate (size, sizes); + register (rd, write, vec, 4); + register (rn, read, gp64, 5); + instruction (); + } + } +} + +// ;; Conversion: integer -> FP. +// MInst.IntToFpu specification configuration. +fn define_int_to_fpu() -> SpecConfig { + let ops: [IntToFpuOp; 8] = [ + IntToFpuOp::I64ToF64, + IntToFpuOp::U64ToF64, + IntToFpuOp::I64ToF32, + IntToFpuOp::U64ToF32, + IntToFpuOp::I32ToF64, + IntToFpuOp::U32ToF64, + IntToFpuOp::I32ToF32, + IntToFpuOp::U32ToF32, + ]; + + spec_config! { + (IntToFpu op rd rn) + { + enumerate (op, ops); + register (rd, write, vec, 4); + register (rn, read, gp, 5); + fpcr (); + flags (); + instruction (); + } + } +} + +// ;; Conversion: integer -> FP. +// MInst.FpuToInt specification configuration. +fn define_fpu_to_int() -> SpecConfig { + let ops: [FpuToIntOp; 8] = [ + FpuToIntOp::F64ToI64, + FpuToIntOp::F64ToU64, + FpuToIntOp::F64ToI32, + FpuToIntOp::F64ToU32, + FpuToIntOp::F32ToI64, + FpuToIntOp::F32ToU64, + FpuToIntOp::F32ToI32, + FpuToIntOp::F32ToU32, + ]; + + spec_config! { + (FpuToInt op rd rn) + { + enumerate (op, ops); + register (rd, write, gp, 4); + register (rn, read, vec, 5); + fpcr (); + flags (); + instruction (); + } + } +} + +fn define_fpu_round() -> SpecConfig { + // FpuRoundMode + let modes = [ + FpuRoundMode::Nearest64, + FpuRoundMode::Nearest32, + FpuRoundMode::Zero64, + FpuRoundMode::Zero32, + FpuRoundMode::Plus64, + FpuRoundMode::Plus32, + FpuRoundMode::Minus64, + FpuRoundMode::Minus32, + ]; + + spec_config! { + (FpuRound op rd rn) + { + enumerate (op, modes); + register (rd, write, fp, 4); + register (rn, read, fp, 5); + fpcr (); + instruction (); + } + } +} + +fn define_fpu_rri() -> SpecConfig { + let ops = [ + ( + 32, + FPUOpRI::UShr32(FPURightShiftImm { + amount: 31, + lane_size_in_bits: 32, + }), + ), + ( + 64, + FPUOpRI::UShr64(FPURightShiftImm { + amount: 63, + lane_size_in_bits: 64, + }), + ), + ]; + // FpuRRI + let mut mappings = Mappings::default(); + mappings + .writes + .insert(aarch64::vreg(4), Mapping::require(spec_fp_reg("rd"))); + mappings + .reads + .insert(aarch64::vreg(5), Mapping::require(spec_fp_reg("rn"))); + + SpecConfig { + term: "MInst.FpuRRI".to_string(), + args: ["fpu_op", "rd", "rn"].map(String::from).to_vec(), + + cases: Cases::Cases( + ops.iter() + .rev() + .map(|(size, fpu_op)| Case { + conds: vec![spec_eq( + spec_field( + "lane_size_in_bits".to_string(), + spec_var("fpu_op".to_string()), + ), + spec_const_bit_vector(*size, 8), + )], + cases: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(Inst::FpuRRI { + fpu_op: *fpu_op, + rd: writable_vreg(4), + rn: vreg(5), + }), + scope: aarch64::state(), + mappings: mappings.clone(), + }), + }) + .collect(), + ), + } +} + +// ;; Variant of FpuRRI that modifies its `rd` +fn define_fpu_rrimod() -> SpecConfig { + let ops = [ + ( + 32, + FPUOpRIMod::Sli32(FPULeftShiftImm { + amount: 31, + lane_size_in_bits: 32, + }), + ), + ( + 64, + FPUOpRIMod::Sli64(FPULeftShiftImm { + amount: 63, + lane_size_in_bits: 64, + }), + ), + ]; + // FpuRRIMod + let mut mappings = Mappings::default(); + mappings + .writes + .insert(aarch64::vreg(4), Mapping::require(spec_fp_reg("rd"))); + mappings + .reads + .insert(aarch64::vreg(4), Mapping::require(spec_fp_reg("ri"))); + mappings + .reads + .insert(aarch64::vreg(5), Mapping::require(spec_fp_reg("rn"))); + + SpecConfig { + term: "MInst.FpuRRIMod".to_string(), + args: ["fpu_op", "rd", "ri", "rn"].map(String::from).to_vec(), + + cases: Cases::Cases( + ops.iter() + .rev() + .map(|(size, fpu_op)| Case { + conds: vec![spec_eq( + spec_field( + "lane_size_in_bits".to_string(), + spec_var("fpu_op".to_string()), + ), + spec_const_bit_vector(*size, 8), + )], + cases: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(Inst::FpuRRIMod { + fpu_op: *fpu_op, + rd: writable_vreg(4), + ri: vreg(4), + rn: vreg(5), + }), + scope: aarch64::state(), + mappings: mappings.clone(), + }), + }) + .collect(), + ), + } +} + +// MInst.MovFromVec specification configuration. +fn define_mov_from_vec() -> SpecConfig { + // ScalarSize + let sizes = [ + ScalarSize::Size8, + ScalarSize::Size16, + ScalarSize::Size32, + ScalarSize::Size64, + ]; + + // MovFromVec + let mut mappings = Mappings::default(); + mappings.writes.insert( + aarch64::gpreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + aarch64::vreg(5), + Mapping::require(spec_as_bit_vector_width(spec_var("rn".to_string()), 128)), + ); + + SpecConfig { + term: "MInst.MovFromVec".to_string(), + args: ["rd", "rn", "idx", "size"].map(String::from).to_vec(), + + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| { + let lanes = 128 / size.ty().bits(); + Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Cases( + (0..lanes) + .map(|idx| Case { + conds: vec![spec_eq( + spec_var("idx".to_string()), + spec_const_bit_vector(idx.into(), 8), + )], + cases: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(Inst::MovFromVec { + rd: writable_xreg(4), + rn: vreg(5), + idx: idx.try_into().unwrap(), + size: *size, + }), + scope: aarch64::state(), + mappings: mappings.clone(), + }), + }) + .collect(), + ), + } + }) + .collect(), + }), + } +} + +// MInst.VecDupImm specification configuration. +// +// Note this specification only handles the 8-bit immediate field of ASIMDMovModImm. +// This is sufficient to handle the limited uses of VecDupImm right now. +// +// TODO: handle all ASIMDMovModImm parameters +fn define_vec_dup_imm() -> Result { + // VectorSize + let sizes = [VectorSize::Size32x2]; + + // Invert + let inverts = [false, true]; + + // Execution scope: define opcode template fields. + let mut scope = aarch64::state(); + let abc_bits = Target::Var("abc".to_string()); + let defgh_bits = Target::Var("defgh".to_string()); + scope.global(abc_bits.clone()); + scope.global(defgh_bits.clone()); + + // Mappings + let mut mappings = flags_mappings(); + mappings.writes.insert( + aarch64::vreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + let imm = spec_field("imm".to_string(), spec_var("imm".to_string())); + mappings.reads.insert( + abc_bits.clone(), + Mapping::require(spec_extract(7, 5, imm.clone())), + ); + mappings.reads.insert( + defgh_bits.clone(), + Mapping::require(spec_extract(4, 0, imm.clone())), + ); + + Ok(SpecConfig { + term: "MInst.VecDupImm".to_string(), + args: ["rd", "imm", "invert", "size"].map(String::from).to_vec(), + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| { + Ok(Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Cases( + inverts + .iter() + .map(|invert| { + Ok(Case { + conds: vec![spec_eq_bool( + spec_var("invert".to_string()), + *invert, + )], + cases: Cases::Instruction(InstConfig { + opcodes: Opcodes::Template(vec_dup_imm_template( + writable_vreg(4), + *invert, + *size, + )?), + scope: scope.clone(), + mappings: mappings.clone(), + }), + }) + }) + .collect::>()?, + ), + }) + }) + .collect::>()?, + }), + }) +} + +fn vec_dup_imm_template(rd: Writable, invert: bool, size: VectorSize) -> Result { + // Assemble a base instruction with a placeholder for the immediate field. + // TODO: handle all ASIMDMovModImm parameters shift, is_64bit, and shift_ones. + let placeholder = ASIMDMovModImm { + imm: 0, + shift: 0, + is_64bit: false, + shift_ones: false, + }; + let base = Inst::VecDupImm { + rd, + imm: placeholder, + invert, + size, + }; + let opcode = aarch64::opcode(&base); + let bits = Bits::from_u32(opcode); + + // Splice in symbolic immediate fields. + let abc = Bits { + segments: vec![Segment::Symbolic("abc".to_string(), 3)], + }; + let template = Bits::splice(&bits, &abc, 16)?; + + let defgh = Bits { + segments: vec![Segment::Symbolic("defgh".to_string(), 5)], + }; + let template = Bits::splice(&template, &defgh, 5)?; + + // Verify template against the assembler. + verify_opcode_template(&template, |assignment: &HashMap| { + let abc = assignment.get("abc").unwrap(); + let defgh = assignment.get("defgh").unwrap(); + let bits = (abc << 5) | defgh; + + let imm = ASIMDMovModImm { + imm: bits.try_into().unwrap(), + shift: 0, + is_64bit: false, + shift_ones: false, + }; + Ok(Inst::VecDupImm { + rd, + imm, + invert, + size, + }) + })?; + + Ok(template) +} + +// MInst.VecRRR specification configuration. +fn define_vec_rrr() -> SpecConfig { + // VecALUOp + let vec_alu_ops = [VecALUOp::Addp]; + + // VectorSize + let sizes = [VectorSize::Size8x16, VectorSize::Size8x8]; + + spec_config! { + (VecRRR alu_op rd rn rm size) + { + enumerate (size, sizes); + enumerate (alu_op, vec_alu_ops); + register (rd, write, vec, 4); + register (rn, read, vec, 5); + register (rm, read, vec, 6); + instruction (); + } + } +} + +// MInst.VecMisc specification configuration. +fn define_vec_misc() -> SpecConfig { + // VecMisc2 + let ops = [VecMisc2::Cnt]; + + // VectorSize + let sizes = [VectorSize::Size8x16, VectorSize::Size8x8]; + + spec_config! { + (VecMisc op rd rn size) + { + enumerate (size, sizes); + enumerate (op, ops); + register (rd, write, vec, 4); + register (rn, read, vec, 5); + instruction (); + } + } +} + +// MInst.VecLanes specification configuration. +fn define_vec_lanes() -> SpecConfig { + // VecLanesOp + let vec_lanes_ops = [VecLanesOp::Uminv, VecLanesOp::Addv]; + + // VectorSize + let sizes = [ + VectorSize::Size8x8, + VectorSize::Size8x16, + VectorSize::Size16x4, + VectorSize::Size16x8, + VectorSize::Size32x4, + ]; + + // VecLanes + let mut mappings = Mappings::default(); + mappings.writes.insert( + aarch64::vreg(4), + Mapping::require(spec_var("rd".to_string())), + ); + mappings.reads.insert( + aarch64::vreg(5), + Mapping::require(spec_var("rn".to_string())), + ); + + SpecConfig { + term: "MInst.VecLanes".to_string(), + args: ["op", "rd", "rn", "size"].map(String::from).to_vec(), + + cases: Cases::Match(Match { + on: spec_var("size".to_string()), + arms: sizes + .iter() + .rev() + .map(|size| Arm { + variant: format!("{size:?}"), + args: Vec::new(), + body: Cases::Match(Match { + on: spec_var("op".to_string()), + arms: vec_lanes_ops + .iter() + .map(|op| Arm { + variant: format!("{op:?}"), + args: Vec::new(), + body: Cases::Instruction(InstConfig { + opcodes: Opcodes::Instruction(Inst::VecLanes { + op: *op, + rd: writable_vreg(4), + rn: vreg(5), + size: *size, + }), + scope: aarch64::state(), + mappings: mappings.clone(), + }), + }) + .collect(), + }), + }) + .collect(), + }), + } +} diff --git a/cranelift/isle/veri/isaspec/src/lib.rs b/cranelift/isle/veri/isaspec/src/lib.rs new file mode 100644 index 000000000000..399a031d1332 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/lib.rs @@ -0,0 +1,9 @@ +pub mod aarch64; +pub mod bits; +pub mod builder; +pub mod configure; +pub mod constraints; +pub mod instructions; +pub mod memory; +pub mod semantics; +pub mod spec; diff --git a/cranelift/isle/veri/isaspec/src/memory.rs b/cranelift/isle/veri/isaspec/src/memory.rs new file mode 100644 index 000000000000..6231b925518f --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/memory.rs @@ -0,0 +1,65 @@ +use crate::constraints::Target; + +pub struct ReadEffect { + pub active: Target, + pub addr: Target, + pub size_bits: Target, + pub value: Target, +} + +impl Default for ReadEffect { + fn default() -> Self { + Self::new() + } +} + +impl ReadEffect { + pub fn new() -> Self { + Self { + active: Target::Field(Box::new(Self::variable()), "ACTIVE".to_string()), + addr: Target::Field(Box::new(Self::variable()), "ADDR".to_string()), + size_bits: Target::Field(Box::new(Self::variable()), "SIZE_BITS".to_string()), + value: Target::Field(Box::new(Self::variable()), "VALUE".to_string()), + } + } + + fn variable() -> Target { + Target::Var("MEMORY_READ_EFFECT".to_string()) + } + + pub fn targets(&self) -> Vec<&Target> { + vec![&self.active, &self.addr, &self.size_bits, &self.value] + } +} + +pub struct SetEffect { + pub active: Target, + pub addr: Target, + pub size_bits: Target, + pub value: Target, +} + +impl Default for SetEffect { + fn default() -> Self { + Self::new() + } +} + +impl SetEffect { + pub fn new() -> Self { + Self { + active: Target::Field(Box::new(Self::variable()), "ACTIVE".to_string()), + addr: Target::Field(Box::new(Self::variable()), "ADDR".to_string()), + size_bits: Target::Field(Box::new(Self::variable()), "SIZE_BITS".to_string()), + value: Target::Field(Box::new(Self::variable()), "VALUE".to_string()), + } + } + + fn variable() -> Target { + Target::Var("MEMORY_SET_EFFECT".to_string()) + } + + pub fn targets(&self) -> Vec<&Target> { + vec![&self.active, &self.addr, &self.size_bits, &self.value] + } +} diff --git a/cranelift/isle/veri/isaspec/src/semantics.rs b/cranelift/isle/veri/isaspec/src/semantics.rs new file mode 100644 index 000000000000..ebca140560ac --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/semantics.rs @@ -0,0 +1,23 @@ +use anyhow::Result; +use cranelift_codegen::isa::aarch64::inst::Inst; +use cranelift_isle_veri_aslp::{ast::Block, client::Client, opcode::Opcode}; +use tracing::debug; + +use crate::aarch64; + +// Fetch semantics for the given Cranelift instruction. +pub fn inst_semantics(inst: &Inst, client: &Client) -> Result { + // Assemble instruction. + let opcode = aarch64::opcode(inst); + + // Debugging. + let asm = aarch64::assembly(inst); + + debug!("inst = {inst:#?}"); + debug!("opcode = {opcode:08x}"); + debug!("asm = {asm}"); + + // Fetch semantics. + let opcode_bits = Opcode::from_u32(opcode); + client.opcode(opcode_bits) +} diff --git a/cranelift/isle/veri/isaspec/src/spec.rs b/cranelift/isle/veri/isaspec/src/spec.rs new file mode 100644 index 000000000000..7a8c966949a5 --- /dev/null +++ b/cranelift/isle/veri/isaspec/src/spec.rs @@ -0,0 +1,388 @@ +use std::collections::{HashMap, HashSet}; + +use anyhow::{Result, bail}; + +use cranelift_isle::ast::{Arm, FieldInit, Ident, ModelType, SpecExpr, SpecOp}; +use cranelift_isle::lexer::Pos; + +pub fn spec_const_int(val: i128) -> SpecExpr { + SpecExpr::ConstInt { + val, + pos: Pos::default(), + } +} + +pub fn spec_const_bool(val: bool) -> SpecExpr { + SpecExpr::ConstBool { + val, + pos: Pos::default(), + } +} + +pub fn spec_true() -> SpecExpr { + spec_const_bool(true) +} + +pub fn spec_false() -> SpecExpr { + spec_const_bool(false) +} + +pub fn spec_const_bit_vector(val: u128, width: usize) -> SpecExpr { + assert!(width > 0); + SpecExpr::ConstBitVec { + val, + width, + pos: Pos::default(), + } +} + +pub fn spec_unary(op: SpecOp, x: SpecExpr) -> SpecExpr { + spec_op(op, vec![x]) +} + +pub fn spec_binary(op: SpecOp, x: SpecExpr, y: SpecExpr) -> SpecExpr { + spec_op(op, vec![x, y]) +} + +pub fn spec_ternary(op: SpecOp, x: SpecExpr, y: SpecExpr, z: SpecExpr) -> SpecExpr { + spec_op(op, vec![x, y, z]) +} + +pub fn spec_not(x: SpecExpr) -> SpecExpr { + spec_unary(SpecOp::Not, x) +} + +pub fn spec_if(c: SpecExpr, t: SpecExpr, e: SpecExpr) -> SpecExpr { + spec_ternary(SpecOp::If, c, t, e) +} + +pub fn spec_eq(x: SpecExpr, y: SpecExpr) -> SpecExpr { + spec_binary(SpecOp::Eq, x, y) +} + +pub fn spec_eq_bool(x: SpecExpr, val: bool) -> SpecExpr { + if val { x } else { spec_not(x) } +} + +pub fn spec_or(args: Vec) -> SpecExpr { + spec_op(SpecOp::Or, args) +} + +pub fn spec_any(xs: Vec) -> SpecExpr { + match xs.len() { + 0 => spec_false(), + 1 => xs[0].clone(), + _ => spec_or(xs), + } +} + +pub fn spec_and(args: Vec) -> SpecExpr { + spec_op(SpecOp::And, args) +} + +pub fn spec_all(xs: Vec) -> SpecExpr { + match xs.len() { + 0 => spec_true(), + 1 => xs[0].clone(), + _ => spec_and(xs), + } +} + +pub fn spec_extract(h: usize, l: usize, x: SpecExpr) -> SpecExpr { + spec_ternary( + SpecOp::Extract, + spec_const_int(h.try_into().unwrap()), + spec_const_int(l.try_into().unwrap()), + x, + ) +} + +pub fn spec_conv_to(w: usize, x: SpecExpr) -> SpecExpr { + spec_binary(SpecOp::ConvTo, spec_const_int(w.try_into().unwrap()), x) +} + +pub fn spec_bv2nat(x: SpecExpr) -> SpecExpr { + spec_unary(SpecOp::BV2Nat, x) +} + +pub fn spec_zero_ext(w: usize, x: SpecExpr) -> SpecExpr { + spec_ext(SpecOp::ZeroExt, w, x) +} + +pub fn spec_sign_ext(w: usize, x: SpecExpr) -> SpecExpr { + spec_ext(SpecOp::SignExt, w, x) +} + +fn spec_ext(ext_op: SpecOp, w: usize, x: SpecExpr) -> SpecExpr { + // Simplify nested extensions. + if let SpecExpr::Op { + ref op, + ref args, + pos: _, + } = x + && *op == ext_op + && let [SpecExpr::ConstInt { val, .. }, n] = args.as_slice() + { + let nw: usize = (*val).try_into().unwrap(); + if w >= nw { + return spec_zero_ext(w, n.clone()); + } + } + + // Base case just constructs zero extension operator. + spec_binary(ext_op, spec_const_int(w.try_into().unwrap()), x) +} + +pub fn spec_op(op: SpecOp, args: Vec) -> SpecExpr { + SpecExpr::Op { + op, + args, + pos: Pos::default(), + } +} + +pub fn spec_enum(name: String, variant: String, args: Vec) -> SpecExpr { + SpecExpr::Enum { + name: spec_ident(name), + variant: spec_ident(variant), + args, + pos: Pos::default(), + } +} + +pub fn spec_enum_unit(name: String, variant: String) -> SpecExpr { + spec_enum(name, variant, Vec::new()) +} + +pub fn spec_as_bit_vector_width(x: SpecExpr, width: usize) -> SpecExpr { + SpecExpr::As { + x: Box::new(x), + ty: ModelType::BitVec(Some(width)), + pos: Pos::default(), + } +} + +pub fn spec_field(field: String, x: SpecExpr) -> SpecExpr { + SpecExpr::Field { + field: spec_ident(field), + x: Box::new(x), + pos: Pos::default(), + } +} + +pub fn spec_discriminator(variant: String, x: SpecExpr) -> SpecExpr { + SpecExpr::Discriminator { + variant: spec_ident(variant), + x: Box::new(x), + pos: Pos::default(), + } +} + +pub fn spec_var(id: String) -> SpecExpr { + SpecExpr::Var { + var: spec_ident(id), + pos: Pos::default(), + } +} + +pub fn spec_with(decls: Vec, body: SpecExpr) -> SpecExpr { + SpecExpr::With { + decls, + body: Box::new(body), + pos: Pos::default(), + } +} + +pub fn spec_idents(ids: &[String]) -> Vec { + ids.iter().cloned().map(spec_ident).collect() +} + +pub fn spec_ident(id: String) -> Ident { + Ident(id, Pos::default()) +} + +#[derive(Clone)] +pub struct Conditions { + pub requires: Vec, + pub provides: Vec, + pub modifies: HashSet, +} + +impl Default for Conditions { + fn default() -> Self { + Self::new() + } +} + +impl Conditions { + pub fn new() -> Self { + Self { + requires: Vec::new(), + provides: Vec::new(), + modifies: HashSet::new(), + } + } + + pub fn merge(cs: Vec) -> Self { + match cs.len() { + 0 => Self::new(), + 1 => cs[0].clone(), + _ => Self { + requires: vec![spec_or( + cs.iter().map(|c| spec_all(c.requires.clone())).collect(), + )], + provides: cs + .iter() + .map(|c| { + spec_binary( + SpecOp::Imp, + spec_all(c.requires.clone()), + spec_all(c.provides.clone()), + ) + }) + .collect(), + modifies: cs.iter().fold(HashSet::new(), |acc, c| &acc | &c.modifies), + }, + } + } +} + +pub fn substitute(expr: SpecExpr, substitutions: &HashMap) -> Result { + Ok(match expr { + // Variable + SpecExpr::Var { ref var, pos: _ } => { + if let Some(substitution) = substitutions.get(&var.0) { + substitution.clone() + } else { + expr + } + } + + // Constants are unchanged. + SpecExpr::ConstInt { .. } | SpecExpr::ConstBitVec { .. } | SpecExpr::ConstBool { .. } => { + expr + } + + // Inline macro introduces a new scope. + SpecExpr::Macro { .. } => expr, + + // Scopes require care to ensure we are not replacing introduced variables. + SpecExpr::Match { x, arms, pos } => SpecExpr::Match { + x: Box::new(substitute(*x, substitutions)?), + arms: arms + .into_iter() + .map( + |Arm { + variant, + args, + body, + pos, + }| { + for arg in &args { + if substitutions.contains_key(&arg.0) { + bail!("substituted variable collides with match arm"); + } + } + Ok(Arm { + variant, + args, + body: substitute(body, substitutions)?, + pos, + }) + }, + ) + .collect::>()?, + pos, + }, + SpecExpr::Let { defs, body, pos } => SpecExpr::Let { + defs: defs + .into_iter() + .map(|(var, expr)| { + if substitutions.contains_key(&var.0) { + bail!("substituted variable collides with let binding"); + } + Ok((var, substitute(expr, substitutions)?)) + }) + .collect::>()?, + body: Box::new(substitute(*body, substitutions)?), + pos, + }, + SpecExpr::With { decls, body, pos } => { + for decl in &decls { + if substitutions.contains_key(&decl.0) { + bail!("substituted variable collides with with scope"); + } + } + SpecExpr::With { + decls, + body: Box::new(substitute(*body, substitutions)?), + pos, + } + } + + // Recurse into child expressions. + SpecExpr::As { x, ty, pos } => SpecExpr::As { + x: Box::new(substitute(*x, substitutions)?), + ty, + pos, + }, + SpecExpr::Field { field, x, pos } => SpecExpr::Field { + field, + x: Box::new(substitute(*x, substitutions)?), + pos, + }, + SpecExpr::Discriminator { variant, x, pos } => SpecExpr::Discriminator { + variant, + x: Box::new(substitute(*x, substitutions)?), + pos, + }, + SpecExpr::Op { op, args, pos } => SpecExpr::Op { + op, + args: args + .into_iter() + .map(|arg| substitute(arg, substitutions)) + .collect::>()?, + pos, + }, + SpecExpr::Pair { l, r, pos } => SpecExpr::Pair { + l: Box::new(substitute(*l, substitutions)?), + r: Box::new(substitute(*r, substitutions)?), + pos, + }, + SpecExpr::Enum { + name, + variant, + args, + pos, + } => SpecExpr::Enum { + name, + variant, + args: args + .into_iter() + .map(|arg| substitute(arg, substitutions)) + .collect::>()?, + pos, + }, + SpecExpr::Expand { name, args, pos } => SpecExpr::Expand { + name, + args: args + .into_iter() + .map(|arg| substitute(arg, substitutions)) + .collect::>()?, + pos, + }, + SpecExpr::Struct { fields, pos } => SpecExpr::Struct { + fields: fields + .into_iter() + .map(|f| { + Ok(FieldInit { + name: f.name, + value: Box::new(substitute(*f.value, substitutions)?), + pos: f.pos, + }) + }) + .collect::>()?, + pos, + }, + }) +} diff --git a/cranelift/isle/veri/test-macros/Cargo.toml b/cranelift/isle/veri/test-macros/Cargo.toml new file mode 100644 index 000000000000..ac7905f3ebae --- /dev/null +++ b/cranelift/isle/veri/test-macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cranelift-isle-veri-test-macros" +version = "0.1.0" +publish = false +edition.workspace = true + +[lib] +proc-macro = true + +[dependencies] +quote = { workspace = true } +syn = { workspace = true, features = ["full"]} +proc-macro2 = { workspace = true } +proc-macro-error = { workspace = true } diff --git a/cranelift/isle/veri/test-macros/src/lib.rs b/cranelift/isle/veri/test-macros/src/lib.rs new file mode 100644 index 000000000000..d91ed0cfbbcc --- /dev/null +++ b/cranelift/isle/veri/test-macros/src/lib.rs @@ -0,0 +1,101 @@ +extern crate proc_macro; + +use std::{collections::HashMap, env, ffi::OsStr, fs, path::PathBuf, str::FromStr}; + +use proc_macro::TokenStream; +use proc_macro_error::{abort_call_site, proc_macro_error}; +use quote::quote; +use syn::{Expr, ExprLit, ItemFn, Lit, Meta, parse_macro_input, punctuated::Punctuated}; + +#[proc_macro_error] +#[proc_macro_attribute] +pub fn file_tests(attrs: TokenStream, input: TokenStream) -> TokenStream { + // Parse attributes. + let metas = parse_macro_input!(attrs with Punctuated::::parse_terminated); + let mut attrs = parse_attrs(&metas); + + let relative_path = attrs.remove("path").unwrap_or(".".to_string()); + let ext = attrs.remove("ext").unwrap_or("test".to_string()); + if !attrs.is_empty() { + let keys: String = attrs.keys().cloned().collect::>().join(", "); + abort_call_site!(format!("unknown keys: {keys}")); + } + + // Parse the input as a function. + let input = proc_macro2::TokenStream::from(input); + + let func_ast: ItemFn = + syn::parse(input.clone().into()).expect("should be able to parse tokens as function"); + + let func_ident = &func_ast.sig.ident; + let func_name = func_ident.to_string(); + + // Locate test data directory. + let crate_dir = PathBuf::from_str( + &env::var("CARGO_MANIFEST_DIR") + .expect("CARGO_MANIFEST_DIR environment variable must be set"), + ) + .expect("CARGO_MANIFEST_DIR should be a valid path"); + let test_data_dir = crate_dir.join(relative_path); + + // Collect files with requested extension. + let mut paths = Vec::new(); + for entry in fs::read_dir(test_data_dir).expect("should be able to read test data directory") { + let entry = entry.expect("invalid directory entry"); + if entry.path().extension() == Some(OsStr::new(&ext)) { + paths.push(entry.path()); + } + } + + if paths.is_empty() { + abort_call_site!("no test case files found"); + } + + // Generate one test case per file. + let test_cases = paths.iter().map(|path| { + let full = path + .to_str() + .expect("test file path should be valid string"); + let test_case_name = path + .file_stem() + .expect("test data path should have a file name") + .to_str() + .expect("test file name should be valid string"); + let test_name = format!("{func_name}_{test_case_name}"); + let test_ident = + proc_macro2::Ident::new(test_name.as_str(), proc_macro2::Span::call_site()); + quote! { + #[test] + fn #test_ident() { + #func_ident(#full); + } + } + }); + + // Combining the function and test cases. + let output = quote! { + #input + + #(#test_cases)* + }; + + output.into() +} + +fn parse_attrs(metas: &Punctuated) -> HashMap { + let mut attrs = HashMap::new(); + for meta in metas.iter() { + if let Meta::NameValue(n) = meta { + let key = n.path.get_ident().unwrap().to_string(); + match &n.value { + Expr::Lit(ExprLit { + lit: Lit::Str(s), .. + }) => { + attrs.insert(key, s.value()); + } + _ => abort_call_site!("attribute values must be string"), + } + } + } + attrs +} diff --git a/cranelift/isle/veri/veri/Cargo.toml b/cranelift/isle/veri/veri/Cargo.toml new file mode 100644 index 000000000000..5f857ead9f9d --- /dev/null +++ b/cranelift/isle/veri/veri/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "cranelift-isle-veri" +version = "0.1.0" +edition.workspace = true + +[dependencies] +cranelift-isle = { version = "*", path = "../../isle/", features = ["fancy-errors", "logging"] } +cranelift-codegen-meta = { version = "*", path = "../../../codegen/meta" } +log = { workspace = true } +env_logger = { workspace = true } +anyhow = { workspace = true, features = ['std', 'backtrace'] } +clap = { workspace = true, features = ['default', 'env'] } +serde = { workspace = true, features = ['derive'] } +serde_json = { workspace = true } +num-bigint = { workspace = true } +num-traits = { workspace = true } +easy-smt = { workspace = true } +rayon = { workspace = true } +tempfile = { workspace = true } + +[dev-dependencies] +cranelift-isle-veri-test-macros = { path = "../test-macros" } diff --git a/cranelift/isle/veri/veri/build.rs b/cranelift/isle/veri/veri/build.rs new file mode 100644 index 000000000000..5271753ad4fe --- /dev/null +++ b/cranelift/isle/veri/veri/build.rs @@ -0,0 +1,38 @@ +use std::env; + +fn write_build_meta() { + let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"); + + // Profile: debug, release, ... + let build_profile = env::var("PROFILE").expect("The PROFILE environment variable must be set"); + + // Git hash + let output = std::process::Command::new("git") + .arg("describe") + .arg("--always") + .arg("--dirty") + .arg("--abbrev=12") + .arg("--exclude") + .arg("*") + .output() + .expect("Failed to execute git describe"); + let output = String::from_utf8(output.stdout).unwrap(); + let git_version = output.trim(); + + std::fs::write( + std::path::Path::new(&out_dir).join("meta.rs"), + format!( + "pub const BUILD_PROFILE: &str = \"{build_profile}\";\n\ + pub const GIT_VERSION: &str = \"{git_version}\";\n\ + " + ), + ) + .unwrap(); +} + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=filetests"); + + write_build_meta(); +} diff --git a/cranelift/isle/veri/veri/filetests/broken/add_double.isle b/cranelift/isle/veri/veri/filetests/broken/add_double.isle new file mode 100644 index 000000000000..a2e15ad88d4d --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/broken/add_double.isle @@ -0,0 +1,20 @@ +; 8-bit value type +(type Value (primitive Value)) +(model Value (type (bv 8))) + +; Top-level test term asserts equality +(decl test (Value) Value) +(spec (test arg) (provide (= result arg))) + +; Add +(decl add (Value Value) Value) +(extern extractor add add) +(spec (add x y) (provide (= result (bvadd x y)))) + +; Double x +(decl double (Value) Value) +(extern constructor double double) +(spec (double x) (provide (= result (bvadd x x)))) + +; Broken rule maps add to double. +(rule test (test (add x y)) (double x)) diff --git a/cranelift/isle/veri/veri/filetests/pass/add_commutative.isle b/cranelift/isle/veri/veri/filetests/pass/add_commutative.isle new file mode 100644 index 000000000000..13c58422768b --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/pass/add_commutative.isle @@ -0,0 +1,20 @@ +; 8-bit value type +(type Value (primitive Value)) +(model Value (type (bv 8))) + +; Top-level test term asserts equality +(decl test (Value) Value) +(spec (test arg) (provide (= result arg))) + +; Add x + y +(decl add_xy (Value Value) Value) +(extern extractor add_xy add_xy) +(spec (add_xy x y) (provide (= result (bvadd x y)))) + +; Add y + x +(decl add_yx (Value Value) Value) +(extern constructor add_yx add_yx) +(spec (add_yx x y) (provide (= result (bvadd y x)))) + +; Test rule commutes addition operands +(rule test (test (add_xy x y)) (add_yx x y)) diff --git a/cranelift/isle/veri/veri/filetests/pass/enum_exhaustive.isle b/cranelift/isle/veri/veri/filetests/pass/enum_exhaustive.isle new file mode 100644 index 000000000000..d19f2b915cb9 --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/pass/enum_exhaustive.isle @@ -0,0 +1,40 @@ +; Test case that relies on the assumption that enum types must equal one of the +; variants. + +; 8-bit value type +(type Value (primitive Value)) +(model Value (type (bv 8))) + +; Operation type. +(type Op (enum (Add) (Mul))) + +; Top-level test term asserts equality +(decl test (Value) Value) +(spec (test arg) (provide (= result arg))) + +; op(x, y) +(decl op_xy (Op Value Value) Value) +(extern extractor op_xy op_xy) +(spec (op_xy op x y) + (provide + (= result (match op + ((Add) (bvadd x y)) + ((Mul) (bvmul x y)) + )) + ) +) + +; op(y, x) +(decl op_yx (Op Value Value) Value) +(extern constructor op_yx op_yx) +(spec (op_yx op x y) + (provide + (= result (match op + ((Add) (bvadd y x)) + ((Mul) (bvmul y x)) + )) + ) +) + +; Test rule commutes operands +(rule test (test (op_xy op x y)) (op_yx op x y)) diff --git a/cranelift/isle/veri/veri/filetests/pass/enum_variant_instantiation.isle b/cranelift/isle/veri/veri/filetests/pass/enum_variant_instantiation.isle new file mode 100644 index 000000000000..52a7aa211d48 --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/pass/enum_variant_instantiation.isle @@ -0,0 +1,45 @@ +; Test case that requires type instantiation for enum variants. + +; Value type of unspecified bit-vector width. +(type Value (primitive Value)) +(model Value (type (bv))) + +; Top-level test term asserts equality +(decl test (Value) Value) +(spec (test arg) (provide (= result arg))) + +; Add 42. +(decl add42 (Value) Value) +(extern extractor add42 add42) +(spec (add42 x) (provide (= result (bvadd x #x2a)))) + +; Operation type. +(type Op + (enum + ; Addition of 42. + (Add42) + + ; Deliberately unused variant. Type inference has no way to infer the + ; widths of the Value fields. + (Unused (val Value)) + ) +) + +; Provide instantiations for the unused variant. +(instantiate Op.Unused + ((args (bv 8)) (ret (named Op))) +) + +; Evaluate operation on a value. +(decl eval (Op Value) Value) +(extern constructor eval eval) +(spec (eval op x) + (provide + (= result (match op + ((Add42) (bvadd #x2a x)) + )) + ) +) + +; Test rule lowers addition to an operation evaluation. +(rule test (test (add42 x)) (eval (Op.Add42) x)) diff --git a/cranelift/isle/veri/veri/filetests/pass/macro_calls_macro.isle b/cranelift/isle/veri/veri/filetests/pass/macro_calls_macro.isle new file mode 100644 index 000000000000..45ffe1fb7d75 --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/pass/macro_calls_macro.isle @@ -0,0 +1,22 @@ +; 8-bit value type +(type Value (primitive Value)) +(model Value (type (bv 8))) + +; Top-level test term asserts equality +(decl test (Value) Value) +(spec (test arg) (provide (= result arg))) + +; Add using plain bvadd spec. +(decl add_plain (Value Value) Value) +(extern extractor add_plain add_plain) +(spec (add_plain x y) (provide (= result (bvadd x y)))) + +; Add using a nested macro. +(macro (apply_op op x y) (op! x y)) + +(decl add_with_macro (Value Value) Value) +(extern constructor add_with_macro add_with_macro) +(spec (add_with_macro x y) (provide (= result (apply_op! (macro (a b) (bvadd a b)) x y)))) + +; Test rule checks plain and macro specs are equivalent. +(rule test (test (add_plain x y)) (add_with_macro x y)) diff --git a/cranelift/isle/veri/veri/filetests/pass/priority_operand_size.isle b/cranelift/isle/veri/veri/filetests/pass/priority_operand_size.isle new file mode 100644 index 000000000000..4d785c1c2686 --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/pass/priority_operand_size.isle @@ -0,0 +1,29 @@ +;; Type of a value, modeled as its bit-width. +(type Type (primitive Type)) +(model Type (type Int)) + +;; Operand size of an instruction. +(type OperandSize (enum Size32 Size64)) + +;; An extractor that only matches types that can fit in 64 bits. +(decl fits_in_32 (Type) Type) +(extern extractor fits_in_32 fits_in_32) +(spec (fits_in_32 ty) (provide (= result ty)) (match (<= result 32))) + +;; An extractor that only matches types that can fit in 64 bits. +(decl fits_in_64 (Type) Type) +(extern extractor fits_in_64 fits_in_64) +(spec (fits_in_64 ty) (provide (= result ty)) (match (<= result 64))) + +;; Helper for calculating the `OperandSize` corresponding to a type. +(decl test (Type) OperandSize) +(spec (test ty) + (provide + (= result (if (<= ty 32) (OperandSize.Size32) (OperandSize.Size64)))) + (require + (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) + +;; Helper rules depend upon priority. +(rule operand_size_32 1 (test (fits_in_32 ty)) (OperandSize.Size32)) +(rule operand_size_64 (test (fits_in_64 ty)) (OperandSize.Size64)) +(attr rule operand_size_32 (veri priority)) diff --git a/cranelift/isle/veri/veri/filetests/pass/provide_only_if_match.isle b/cranelift/isle/veri/veri/filetests/pass/provide_only_if_match.isle new file mode 100644 index 000000000000..60c2d82c3c89 --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/pass/provide_only_if_match.isle @@ -0,0 +1,38 @@ +; Test case that the provides of a partial function are assumed only on match. +; +; Contrived regression test for incorrect extractor semantics. + +; 8-bit value type +(type Value (primitive Value)) +(model Value (type (bv 8))) + +; Coin flip result. +(type Coin (enum Heads Tails)) + +; Top-level test term selects a result based on parity. +(decl test (Value) Coin) +(spec (test v) (provide + (= result + (if (= (extract 0 0 v) #b1) + (Coin.Heads) + (Coin.Tails) + ) + ) + ) +) + +; Contrived extractor matches on odd and provides it must be 73. +(decl odd73 () Value) +(extern extractor odd73 odd73) +(spec (odd73) + (match (= (extract 0 0 result) #b1)) + (provide (= result #x41)) +) + +; Lowering to heads when odd73 matches should still be correct. +(rule test_odd73_heads 1 (test (odd73)) (Coin.Heads)) + +; Accounting for priority should negate the matches of odd73, but not the +; provides. +(rule test_tails (test v) (Coin.Tails)) +(attr rule test_odd73_heads (veri priority)) diff --git a/cranelift/isle/veri/veri/filetests/pass/type_qualifier.isle b/cranelift/isle/veri/veri/filetests/pass/type_qualifier.isle new file mode 100644 index 000000000000..383aec6712c0 --- /dev/null +++ b/cranelift/isle/veri/veri/filetests/pass/type_qualifier.isle @@ -0,0 +1,25 @@ +; Test case that requires type qualifier to satisfy type inference. + +; Value type of unspecified bit-vector width. +(type Value (primitive Value)) +(model Value (type (bv))) + +; Top-level test term asserts equality +(decl test (Value) Value) +(spec (test arg) (provide (= result arg))) + +; Add then mask low byte. +; +; Note the (as ...) type qualifier. Without it, this test case would have +; underconstrained type inference. +(decl add_then_mask (Value Value) Value) +(extern extractor add_then_mask add_then_mask) +(spec (add_then_mask x y) (provide (= result (extract 7 0 (bvadd x (as y (bv 16))))))) + +; Mask low byte then add. +(decl mask_then_add (Value Value) Value) +(extern constructor mask_then_add mask_then_add) +(spec (mask_then_add x y) (provide (= result (bvadd (extract 7 0 x) (extract 7 0 y))))) + +; Test rule swaps mask order. +(rule test (test (add_then_mask x y)) (mask_then_add x y)) diff --git a/cranelift/isle/veri/veri/script/expand.sh b/cranelift/isle/veri/veri/script/expand.sh new file mode 100755 index 000000000000..b1a441ccd9a6 --- /dev/null +++ b/cranelift/isle/veri/veri/script/expand.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +function expand() { + cargo run --bin expand -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir /tmp \ + "$@" +} + +rm -f output/*.out + +expand \ + --name aarch64 \ + --term-name sink_load_into_addr \ + > output/sink_load_into_addr.out + +expand \ + --name aarch64 \ + --term-name sink_load_into_addr \ + --chain add_imm_to_addr \ + > output/sink_load_into_addr_inline_add_imm_to_addr.out + +expand \ + --name aarch64 \ + --term-name sink_load_into_addr \ + --chain add_imm_to_addr \ + --chain add_imm \ + > output/sink_load_into_addr_inline_add_imm_to_addr_add_imm.out + +expand \ + --name aarch64 \ + --term-name sink_load_into_addr \ + --maximal-chaining \ + > output/sink_load_into_addr_maximal_inlining.out + +expand \ + --name aarch64 \ + --term-name lower \ + > output/lower.out + +expand \ + --name aarch64 \ + --term-name lower \ + --no-expand-internal-extractors \ + > output/lower_internal_extractors.out + +expand \ + --name aarch64 \ + --term-name lower \ + --no-expand-internal-extractors \ + --maximal-chaining \ + --max-rules 1 \ + > output/lower_internal_extractors_maximal_inline_1.out + +expand \ + --name aarch64 \ + --term-name lower \ + --no-expand-internal-extractors \ + --maximal-chaining \ + --max-rules 2 \ + --exclude-chain operand_size \ + > output/lower_internal_extractors_maximal_inline_2.out + +expand \ + --name aarch64 \ + --term-name lower \ + --no-expand-internal-extractors \ + --maximal-chaining \ + --max-rules 3 \ + --exclude-chain operand_size \ + > output/lower_internal_extractors_maximal_inline_3.out + +expand \ + --name aarch64 \ + --term-name lower \ + --no-expand-internal-extractors \ + --maximal-chaining \ + --max-rules 3 \ + --exclude-chain operand_size \ + --no-prune-infeasible \ + > output/lower_internal_extractors_no_prune_maximal_inline_3.out + +expand \ + --name aarch64 \ + --term-name lower \ + --no-expand-internal-extractors \ + --maximal-chaining \ + --max-rules 6 \ + --exclude-chain operand_size \ + > output/lower_internal_extractors_maximal_inline_6.out + +expand \ + --name x64 \ + --term-name lower \ + --no-expand-internal-extractors \ + > output/x64_lower_internal_extractors.out + +expand \ + --name x64 \ + --term-name lower \ + --no-expand-internal-extractors \ + --chain to_amode_add \ + --chain amode_imm_reg_reg_shift \ + > output/x64_lower_internal_extractors_amode_inlining.out diff --git a/cranelift/isle/veri/veri/script/explorer.sh b/cranelift/isle/veri/veri/script/explorer.sh new file mode 100755 index 000000000000..7121aadd7722 --- /dev/null +++ b/cranelift/isle/veri/veri/script/explorer.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +export RUST_LOG=info + +# Options +function usage() { + echo "Usage: ${0} [-h] [-o ]" + exit 2 +} + +output_dir="${ISLE_EXPLORER_OUTPUT_DIR:-}" +while getopts "o:h" opt; do + case "${opt}" in + o) output_dir="${OPTARG}" ;; + h) usage ;; + *) usage ;; + esac +done + +# Setup output. +if [[ -z "${output_dir}" ]]; then + echo "output directory not set" + exit 2 +fi +mkdir -p "${output_dir}" + +# Generate explorer. +for arch in aarch64 x64; do + arch_dir="${output_dir:?}/${arch:?}" + rm -rf "${arch_dir}" + cargo run --bin explorer -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir /tmp \ + --name "${arch}" \ + --output-dir "${arch_dir}" +done diff --git a/cranelift/isle/veri/veri/script/generated_isle_files.sh b/cranelift/isle/veri/veri/script/generated_isle_files.sh new file mode 100755 index 000000000000..ff2962dc4444 --- /dev/null +++ b/cranelift/isle/veri/veri/script/generated_isle_files.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +find ../../.. -name '*.isle' | xargs grep -l 'GENERATED BY' diff --git a/cranelift/isle/veri/veri/script/graph.sh b/cranelift/isle/veri/veri/script/graph.sh new file mode 100755 index 000000000000..811bf1d6602c --- /dev/null +++ b/cranelift/isle/veri/veri/script/graph.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +# Clean. +rm -f output/*.{dot,svg} + +# Rules. +arch="aarch64" +rules=( + "iadd_base_case" + "iadd_imm12_right" + "iadd_imm12_left" + "iadd_i128" +) + +for rule in "${rules[@]}"; do + name="${arch}_${rule}" + + # Generate dot. + dot_path="output/${name}.dot" + cargo run --bin graph -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir /tmp \ + --name "${arch}" \ + --rule "${rule}" \ + | tee "output/${name}.dot" + + # Render. + svg_path="output/${name}.svg" + dot -Tsvg "${dot_path}" >"${svg_path}" +done diff --git a/cranelift/isle/veri/veri/script/install/aslp.sh b/cranelift/isle/veri/veri/script/install/aslp.sh new file mode 100755 index 000000000000..6d926bc40e49 --- /dev/null +++ b/cranelift/isle/veri/veri/script/install/aslp.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +# Defaults. +repo="mmcloughlin/aslp" +version="8ca39c0f1b7f4b588fc840aa1a9fbbc5b9085ad0" +ocaml_compiler="4.14.2" + +# Options +function usage() { + echo "Usage: ${0} -i [-h] [-r ] [-v ] [-c ] [-t ]" + exit 2 +} + +install_dir="" +tmp_dir="" +while getopts "r:v:c:i:t:h" opt; do + case "${opt}" in + r) repo="${OPTARG}" ;; + v) version="${OPTARG}" ;; + c) ocaml_compiler="${OPTARG}" ;; + i) install_dir="${OPTARG}" ;; + t) tmp_dir="${OPTARG}" ;; + h) usage ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# Check install directory. +if [[ ! -d "${install_dir}" ]]; then + echo "install directory does not exist" + exit 1 +fi + +# Setup temp directory. +if [[ -z "${tmp_dir}" ]]; then + tmp_dir=$(mktemp -d) +fi + +if [[ ! -d "${tmp_dir}" ]]; then + echo "temporary directory does not exist" + exit 1 +fi + +pushd "${tmp_dir}" + +# Ensure opam is installed. +if ! command -v opam &> /dev/null; then + echo "opam is not installed" + exit 1 +fi + +# # Setup opam root. +export OPAMROOT="${install_dir}/.opam" +export OPAMYES="true" +opam init --compiler="${ocaml_compiler}" + +eval $(opam env) + +# Download and extract ASLp. +archive_name="${version}.tar.gz" +archive_url="https://github.com/${repo}/archive/${version}.tar.gz" +wget --quiet "${archive_url}" + +tar xvzf \ + "${archive_name}" \ + --strip-components=1 + +# Install. +export DUNE_INSTALL_PREFIX="${install_dir}" +opam install . --deps-only --with-test +opam exec -- dune build +opam exec -- dune install diff --git a/cranelift/isle/veri/veri/script/install/cvc5.sh b/cranelift/isle/veri/veri/script/install/cvc5.sh new file mode 100755 index 000000000000..74c4009a9c1e --- /dev/null +++ b/cranelift/isle/veri/veri/script/install/cvc5.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +# Options +function usage() { + echo "Usage: ${0} -i [-h] [-v ] [-t ]" + exit 2 +} + +version="1.2.0" +install_dir="" +tmp_dir="" +while getopts "v:i:t:h" opt; do + case "${opt}" in + v) version="${OPTARG}" ;; + i) install_dir="${OPTARG}" ;; + t) tmp_dir="${OPTARG}" ;; + h) usage ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# Check install directory. +if [[ ! -d "${install_dir}" ]]; then + echo "install directory does not exist" + exit 1 +fi + +# Setup temp directory. +if [[ -z "${tmp_dir}" ]]; then + tmp_dir=$(mktemp -d) +fi + +if [[ ! -d "${tmp_dir}" ]]; then + echo "temporary directory does not exist" + exit 1 +fi + +pushd "${tmp_dir}" + +# Determine which release build. +platform=$(uname -sm) +if [[ "${platform}" == "Darwin arm64" ]]; then + arch="arm64" + os="macOS" + mode="shared" +elif [[ "${platform}" == "Linux x86_64" ]]; then + arch="x86_64" + os="Linux" + mode="static" +else + echo "unsupported platform ${platform}" + exit 1 +fi + +# Download. +archive_stem="cvc5-${os}-${arch}-${mode}" +archive_name="${archive_stem}.zip" +url="https://github.com/cvc5/cvc5/releases/download/cvc5-${version}/${archive_name}" +wget --quiet -O "${archive_name}" "${url}" + +# Extract. +unzip "${archive_name}" + +# Install. +cp -r "${archive_stem}"/* "${install_dir}/" diff --git a/cranelift/isle/veri/veri/script/install/z3.sh b/cranelift/isle/veri/veri/script/install/z3.sh new file mode 100755 index 000000000000..360131200fd1 --- /dev/null +++ b/cranelift/isle/veri/veri/script/install/z3.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Options +function usage() { + echo "Usage: ${0} -b [-h] [-v ] [-t ]" + exit 2 +} + +version="4.13.0" +bin_dir="" +tmp_dir="" +while getopts "v:b:t:h" opt; do + case "${opt}" in + v) version="${OPTARG}" ;; + b) bin_dir="${OPTARG}" ;; + t) tmp_dir="${OPTARG}" ;; + h) usage ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# Check binary install directory. +if [[ ! -d "${bin_dir}" ]]; then + echo "binary install directory does not exist" + exit 1 +fi + +# Setup temp directory. +if [[ -z "${tmp_dir}" ]]; then + tmp_dir=$(mktemp -d) +fi + +if [[ ! -d "${tmp_dir}" ]]; then + echo "temporary directory does not exist" + exit 1 +fi + +pushd "${tmp_dir}" + +# Determine which release build. +platform=$(uname -sm) +if [[ "${platform}" == "Darwin arm64" ]]; then + arch="arm64" + os="osx-11.0" +elif [[ "${platform}" == "Linux x86_64" ]]; then + arch="x64" + os="glibc-2.31" +else + echo "unsupported platform ${platform}" + exit 1 +fi + +# Download. +archive_stem="z3-${version}-${arch}-${os}" +archive_name="${archive_stem}.zip" +url="https://github.com/Z3Prover/z3/releases/download/z3-${version}/${archive_name}" +wget --quiet -O "${archive_name}" "${url}" + +# Extract. +unzip "${archive_name}" + +# Install. +cp "${archive_stem}/bin/z3" "${bin_dir}" diff --git a/cranelift/isle/veri/veri/script/reachable.sh b/cranelift/isle/veri/veri/script/reachable.sh new file mode 100755 index 000000000000..b13b79cc7d78 --- /dev/null +++ b/cranelift/isle/veri/veri/script/reachable.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +function reachable() { + local arch=$1 + cargo run --bin reachable -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir /tmp \ + --name "${arch}" \ + > "output/${arch}.reachable" +} + +rm -f output/*.reachable + +reachable "aarch64" +reachable "x64" diff --git a/cranelift/isle/veri/veri/script/serve.sh b/cranelift/isle/veri/veri/script/serve.sh new file mode 100755 index 000000000000..6fae25f3bfa6 --- /dev/null +++ b/cranelift/isle/veri/veri/script/serve.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +# Options +function usage() { + echo "Usage: ${0} [-h] [-o ] [-p ]" + exit 2 +} + +output_dir="${ISLE_EXPLORER_OUTPUT_DIR:-}" +port="5050" +while getopts "o:p:h" opt; do + case "${opt}" in + o) output_dir="${OPTARG}" ;; + p) port="${OPTARG}" ;; + h) usage ;; + *) usage ;; + esac +done + +if [[ ! -d "${output_dir}" ]]; then + echo "output directory does not exist" + exit 1 +fi + +# Serve. +miniserve \ + --port "${port}" \ + --index index.html \ + --disable-indexing \ + --verbose \ + "${output_dir}" diff --git a/cranelift/isle/veri/veri/script/spec_lines.sh b/cranelift/isle/veri/veri/script/spec_lines.sh new file mode 100755 index 000000000000..f87a4b78f85a --- /dev/null +++ b/cranelift/isle/veri/veri/script/spec_lines.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +RUST_LOG=isle_spec_lines=trace cargo run --bin spec_lines -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir /tmp \ + --name aarch64 \ + 2>&1 | grep SPEC_LINES diff --git a/cranelift/isle/veri/veri/script/status.sh b/cranelift/isle/veri/veri/script/status.sh new file mode 100755 index 000000000000..249c262cb619 --- /dev/null +++ b/cranelift/isle/veri/veri/script/status.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cargo run --bin status -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir /tmp \ + "$@" diff --git a/cranelift/isle/veri/veri/script/veri.sh b/cranelift/isle/veri/veri/script/veri.sh new file mode 100755 index 000000000000..bf3ba763dd91 --- /dev/null +++ b/cranelift/isle/veri/veri/script/veri.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Options +function usage() { + echo "Usage: ${0} [-h] [-a ] [-t ] [-o ] [-p ]" + exit 2 +} + +arch="aarch64" +tmp_dir="" +output_dir="output" +profile="dev" +while getopts "a:t:o:p:h" opt; do + case "${opt}" in + a) arch="${OPTARG}" ;; + t) tmp_dir="${OPTARG}" ;; + o) output_dir="${OPTARG}" ;; + p) profile="${OPTARG}" ;; + h) usage ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# Setup output. +mkdir -p "${output_dir}" + +# Setup temp directory. +if [[ -z "${tmp_dir}" ]]; then + tmp_dir=$(mktemp -d) +fi + +if [[ ! -d "${tmp_dir}" ]]; then + echo "temporary directory does not exist" + exit 1 +fi + +# Run. +cargo run --bin veri --profile "${profile}" -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir "${tmp_dir}" \ + --name "${arch}" \ + --log-dir "${output_dir}/log" \ + "$@" \ + | tee "${output_dir}/${arch}.veri" diff --git a/cranelift/isle/veri/veri/script/verify/ci.sh b/cranelift/isle/veri/veri/script/verify/ci.sh new file mode 100755 index 000000000000..1dacec1506b0 --- /dev/null +++ b/cranelift/isle/veri/veri/script/verify/ci.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -euo pipefail + +RUST_LOG=info ./script/veri.sh "$@" -- \ + --num-threads 0 \ + --results-to-log-dir \ + --ignore-solver-tags \ + --filter include:first-rule-named \ + --filter exclude:tag:vector \ + --filter exclude:tag:slow \ + --filter exclude:tag:i128 \ + --filter exclude:tag:atomics \ + --filter exclude:tag:narrowfloat \ + --filter exclude:tag:amode_const \ + ; diff --git a/cranelift/isle/veri/veri/script/verify/eval.sh b/cranelift/isle/veri/veri/script/verify/eval.sh new file mode 100755 index 000000000000..01a3bcd17ff2 --- /dev/null +++ b/cranelift/isle/veri/veri/script/verify/eval.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Options. +function usage() { + echo "Usage: ${0} -n -t " + exit 2 +} + +name="adhoc" +timeout=60 +mode="full" +while getopts "n:t:c" opt; do + case "${opt}" in + n) name="${OPTARG}" ;; + t) timeout="${OPTARG}" ;; + c) mode="ci" ;; + *) usage ;; + esac +done + +[[ -n "${name}" ]] +[[ -n "${EVAL_DATA_DIR-data}" ]] + +# Metadata helpers. +function json_new() { + local file="${1}" + echo '{}' >"${file}" +} + +function json_set() { + local file="${1}" + local key="${2}" + local value="${3}" + jq '. += $ARGS.named' --arg "${key}" "${value}" "${file}" | sponge "${file}" +} + +# Setup temporary directory. +tmp_dir=$(mktemp -d) + +# Setup results directory. +timestamp=$(date -u '+%Y-%m-%dT%T') +output_dir="${EVAL_DATA_DIR-data}/run/${timestamp}-${name}" +mkdir -p "${output_dir}" + +# Save metadata +metadata_file="${output_dir}/metadata.json" +json_new "${metadata_file}" +json_set "${metadata_file}" "name" "${name}" +json_set "${metadata_file}" "timestamp" "${timestamp}" +json_set "${metadata_file}" "timeout" "${timeout}" +json_set "${metadata_file}" "hostname" "$(hostname)" + +z3_version=$(z3 --version) +json_set "${metadata_file}" "z3_version" "${z3_version}" + +cvc5_version=$(cvc5 --version | head -n 1) +json_set "${metadata_file}" "cvc5_version" "${cvc5_version}" + +# System information. +system_dir="${output_dir}/sys/" +mkdir -p "${system_dir}" +lscpu >"${system_dir}/lscpu.out" +cp /proc/cpuinfo "${system_dir}/cpuinfo" + +# Clean build +cargo clean + +# Eval +extra_args=() +case "${mode}" in + "ci") extra_args+=("--ignore-solver-tags") ;; +esac + +RUST_LOG=info \ +cargo run --bin veri --release -- \ + --codegen-crate-dir ../../../codegen/ \ + --work-dir "${tmp_dir}" \ + --name aarch64 \ + --log-dir "${output_dir}/log" \ + --results-to-log-dir \ + --timeout "${timeout}" \ + --num-threads 0 \ + --no-skip-todo \ + "${extra_args[@]}" \ + \ + --filter include:tag:wasm_proposal_mvp \ + --filter exclude:tag:wasm_category_stack \ + --filter exclude:not:root:lower \ + --filter exclude:tag:vector \ + --filter exclude:tag:atomics \ + --filter exclude:tag:spectre \ + --filter exclude:tag:narrowfloat \ + --filter include:tag:clif_popcnt \ + --filter exclude:tag:amode_const \ + --filter exclude:tag:i128 \ + \ + --filter include:root:emit_side_effect \ + --filter include:root:operand_size \ + --filter include:root:scalar_size \ + --filter include:root:size_from_ty \ + ; diff --git a/cranelift/isle/veri/veri/src/assets/style.css b/cranelift/isle/veri/veri/src/assets/style.css new file mode 100644 index 000000000000..69e55b7f7825 --- /dev/null +++ b/cranelift/isle/veri/veri/src/assets/style.css @@ -0,0 +1,143 @@ +:root { + font-size: 15px; + + --invert-font-color: #fff; + --primary-color: #00a0ad; + --light-color: #bbb; +} + +body { + width: 100%; + margin: 2em 0; + line-height: 1.6; + font-family: monospace; + font-size: 1rem; +} + +body>header, +body>main, +body>footer { + width: 100%; + margin-right: auto; + margin-left: auto; + max-width: 950px; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: sans-serif; + font-weight: 700; +} + +h1 { + font-size: 2rem; + line-height: 1.125; +} + +h2 { + font-size: 1.75rem; + line-height: 1.15; +} + +h3 { + font-size: 1.5rem; + line-height: 1.175; +} + +h4 { + font-size: 1.25rem; + line-height: 1.2; +} + +h5 { + font-size: 1.125rem; + line-height: 1.225; +} + +h6 { + font-size: 1rem; + line-height: 1.25; +} + +a { + color: var(--primary-color); + text-decoration: underline; +} + +a:hover { + background-color: var(--primary-color); + color: var(--invert-font-color); + text-decoration: none; +} + +menu { + margin: 0; + padding: 0; + border-top: 1px solid var(--light-color); +} + +menu li { + list-style-type: none; + margin: 0; + padding: 0; + border-bottom: 1px solid var(--light-color); +} + +menu a { + display: block; + padding: 1rem; +} + +table { + width: 100%; + border-collapse: collapse; +} + +td, +th { + padding: .2rem; + border-bottom: 1px solid var(--light-color); +} + +th { + text-align: left; +} + +table .id { + text-align: right; + width: 1em; + white-space: nowrap; +} + +pre { + counter-reset: line; +} + +pre code { + counter-increment: line; + position: relative; +} + +pre code::before { + content: counter(line); + user-select: none; + color: var(--light-color); + display: inline-block; + position: absolute; + left: -5em; + width: 4em; + text-align: right; +} + +pre code:target::before { + color: var(--primary-color); + font-weight: bold; +} + +.id { + color: var(--light-color); +} diff --git a/cranelift/isle/veri/veri/src/bin/count.rs b/cranelift/isle/veri/veri/src/bin/count.rs new file mode 100644 index 000000000000..26b38f8d4831 --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/count.rs @@ -0,0 +1,248 @@ +use std::collections::{BTreeSet, HashMap, HashSet}; + +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle::{ + sema::TermId, + trie_again::{BindingId, Rule, RuleSet}, +}; +use cranelift_isle_veri::{ + program::Program, + reachability::{self, Reachability}, +}; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, + + /// Whether to disable expansion of internal extractors. + #[arg(long)] + no_expand_internal_extractors: bool, + + /// Term to count. + #[arg(long, required = true)] + term_name: String, + + /// Maximum rules: only expand terms with at most this many rules. + #[arg(long, default_value = "0")] + max_rules: usize, + + /// Terms to exclude from chaining. + #[arg(long, value_name = "TERM_NAME")] + exclude_chain: Vec, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let prog = Program::from_files(&inputs, !opts.no_expand_internal_extractors)?; + + // Derive rule sets. + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + + // Lookup term to count. + let root_term_id = prog + .get_term_by_name(opts.term_name.as_str()) + .ok_or(format_err!("unknown term {}", opts.term_name))?; + println!("term = {}", opts.term_name); + println!("id = {}", root_term_id.index()); + + // Count expansions. + let mut expansion_counter = ExpansionCounter::new(&prog, &term_rule_sets); + expansion_counter.enable_expansion(root_term_id); + if opts.max_rules > 0 { + expansion_counter.set_max_rules(opts.max_rules); + } + for exclude_term_name in &opts.exclude_chain { + let exclude_term_id = prog + .get_term_by_name(exclude_term_name) + .ok_or(format_err!("unknown term {exclude_term_name}"))?; + expansion_counter.disable_expansion(exclude_term_id); + } + + let n = expansion_counter.term(root_term_id, ""); + println!("expansions = {n}"); + + Ok(()) +} + +struct ExpansionCounter<'a> { + prog: &'a Program, + term_rule_sets: &'a HashMap, + reach: Reachability, + + enable_expansion: HashSet, + disable_expansion: HashSet, + max_rules: usize, +} + +impl<'a> ExpansionCounter<'a> { + fn new(prog: &'a Program, term_rule_sets: &'a HashMap) -> Self { + Self { + prog, + term_rule_sets, + reach: Reachability::build(term_rule_sets), + + enable_expansion: HashSet::new(), + disable_expansion: HashSet::new(), + max_rules: usize::MAX, + } + } + + fn term(&mut self, term_id: TermId, indent: &str) -> usize { + println!( + "{indent}> {term_name}", + term_name = self.prog.term_name(term_id) + ); + + let n = if !self.may_expand(term_id) { + 1 + } else { + let rule_set = &self.term_rule_sets[&term_id]; + self.rule_set(rule_set, indent) + }; + + if n > 1 { + println!( + "{indent}< {term_name} = {n}", + term_name = self.prog.term_name(term_id) + ); + } + + n + } + + fn enable_expansion(&mut self, term_id: TermId) { + self.enable_expansion.insert(term_id); + } + + fn disable_expansion(&mut self, term_id: TermId) { + self.disable_expansion.insert(term_id); + } + + fn set_max_rules(&mut self, max_rules: usize) { + self.max_rules = max_rules; + } + + fn may_expand(&mut self, term_id: TermId) -> bool { + if !self.term_rule_sets.contains_key(&term_id) { + return false; + } + + if self.reach.is_cyclic(term_id) { + return false; + } + + if self.enable_expansion.contains(&term_id) { + return true; + } + + if self.disable_expansion.contains(&term_id) { + return false; + } + + let rule_set = &self.term_rule_sets[&term_id]; + if rule_set.rules.len() > self.max_rules { + return false; + } + + true + } + + fn rule_set(&mut self, rule_set: &RuleSet, indent: &str) -> usize { + let mut n = 0; + for rule in &rule_set.rules { + let r = self.rule(rule_set, rule, indent); + n += r; + println!( + "{indent}n={n} r={r} rule={}", + rule.pos.pretty_print_line(&self.prog.files) + ); + } + n + } + + fn rule(&mut self, rule_set: &RuleSet, rule: &Rule, indent: &str) -> usize { + let binding_ids = rule_bindings(rule_set, rule); + let mut n = 1; + for binding_id in binding_ids { + let binding = &rule_set.bindings[binding_id.index()]; + if let Some(term_id) = reachability::binding_used_term(binding) { + n *= self.term(term_id, &format!("{indent}.\t")); + } + } + n + } +} + +fn rule_bindings(rule_set: &RuleSet, rule: &Rule) -> BTreeSet { + // TODO(mbm): duplicates logic in expand::Application + + // Initialize stack of bindings used directly by the rule. + let mut stack = Vec::new(); + + // Result binding. + stack.push(rule.result); + + // Constraints and equality. + for i in 0..rule_set.bindings.len() { + let binding_id = i.try_into().unwrap(); + + if rule.get_constraint(binding_id).is_some() { + stack.push(binding_id); + } + + if let Some(equal_binding_id) = rule.equals.find(binding_id) { + stack.push(equal_binding_id); + } + } + + // TODO(mbm): iterators, prio? + + // Impure. + stack.extend(&rule.impure); + + // Collect dependencies. + let mut binding_ids = BTreeSet::new(); + while let Some(binding_id) = stack.pop() { + if binding_ids.contains(&binding_id) { + continue; + } + binding_ids.insert(binding_id); + + let binding = &rule_set.bindings[binding_id.index()]; + stack.extend(binding.sources()); + } + + binding_ids +} diff --git a/cranelift/isle/veri/veri/src/bin/expand.rs b/cranelift/isle/veri/veri/src/bin/expand.rs new file mode 100644 index 000000000000..0c3afb77ca9e --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/expand.rs @@ -0,0 +1,99 @@ +use std::collections::HashMap; + +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle_veri::debug::print_expansion; +use cranelift_isle_veri::expand::{Chaining, Expander}; +use cranelift_isle_veri::program::Program; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, + + /// Whether to disable expansion of internal extractors. + #[arg(long)] + no_expand_internal_extractors: bool, + + /// Term to expand. + #[arg(long, required = true)] + term_name: String, + + /// Whether to disable pruning of infeasible expansions. + #[arg(long)] + no_prune_infeasible: bool, + + /// Term names to chain. + #[arg(long, value_name = "TERM_NAME")] + chain: Vec, + + /// Whether to enable maximal chaining. + #[arg(long)] + maximal_chaining: bool, + + /// Maximum rules: only chain terms with at most this many rules. + #[arg(long, default_value = "0")] + max_rules: usize, + + /// Terms to exclude from chaining. + #[arg(long, value_name = "TERM_NAME")] + exclude_chain: Vec, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let prog = Program::from_files(&inputs, !opts.no_expand_internal_extractors)?; + + // Configure chaining. + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + let mut chaining = Chaining::new(&prog, &term_rule_sets)?; + chaining.chain_terms(&opts.chain)?; + chaining.set_default(opts.maximal_chaining); + chaining.set_max_rules(opts.max_rules); + chaining.exclude_chain_terms(&opts.exclude_chain)?; + + // Build expansions. + let mut expander = Expander::new(&prog, &term_rule_sets, chaining); + expander.add_root_term_name(&opts.term_name)?; + expander.set_prune_infeasible(!opts.no_prune_infeasible); + expander.expand(); + + // Report. + let expansions = expander.expansions(); + println!("expansions = {}", expansions.len()); + for expansion in expansions { + print_expansion(&prog, expansion); + } + + Ok(()) +} diff --git a/cranelift/isle/veri/veri/src/bin/explorer.rs b/cranelift/isle/veri/veri/src/bin/explorer.rs new file mode 100644 index 000000000000..58bdbfde5acf --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/explorer.rs @@ -0,0 +1,88 @@ +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle_veri::{ + expand::{Chaining, Expander}, + explorer::ExplorerWriter, + program::Program, +}; +use std::collections::HashMap; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, + + /// Output directory for explorer files. + #[arg(long, required = true)] + output_dir: std::path::PathBuf, + + /// Whether to enable graph generation. + #[arg(long, env = "ISLE_EXPLORER_GRAPHS")] + graphs: bool, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let _ = env_logger::try_init(); + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let root_term = if opts.name != "opt" { + "lower" + } else { + "simplify" + }; + + let expand_internal_extractors = false; + let prog = Program::from_files(&inputs, expand_internal_extractors)?; + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + + // Generate expansions. + // TODO(mbm): don't hardcode the expansion configuration + let chaining = Chaining::new(&prog, &term_rule_sets)?; + let mut expander = Expander::new(&prog, &term_rule_sets, chaining); + expander.add_root_term_name(root_term)?; + expander.set_prune_infeasible(true); + expander.expand(); + + // Generate explorer. + let mut explorer_writer = ExplorerWriter::new( + opts.output_dir, + &prog, + expander.chaining(), + expander.expansions(), + ); + if opts.graphs { + explorer_writer.enable_graphs(); + } + explorer_writer.write()?; + + Ok(()) +} diff --git a/cranelift/isle/veri/veri/src/bin/graph.rs b/cranelift/isle/veri/veri/src/bin/graph.rs new file mode 100644 index 000000000000..464da1ba1238 --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/graph.rs @@ -0,0 +1,115 @@ +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle::trie_again::BindingId; +use cranelift_isle_veri::{ + debug::binding_string, + expand::{Chaining, Expander, Expansion}, + program::Program, +}; +use std::collections::HashMap; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, + + /// Filter to expansions involving this rule. + #[arg(long, required = true)] + rule: String, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let _ = env_logger::try_init(); + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let root_term = if opts.name != "opt" { + "lower" + } else { + "simplify" + }; + let expand_internal_extractors = false; + let prog = Program::from_files(&inputs, expand_internal_extractors)?; + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + + // Lookup target rule. + let rule = prog.get_rule_by_identifier(&opts.rule).ok_or(format_err!( + "unknown rule: {rule_name}", + rule_name = opts.rule + ))?; + + // Generate expansions. + // TODO(mbm): don't hardcode the expansion configuration + let chaining = Chaining::new(&prog, &term_rule_sets)?; + let mut expander = Expander::new(&prog, &term_rule_sets, chaining); + expander.add_root_term_name(root_term)?; + expander.set_prune_infeasible(true); + expander.expand(); + + // Process expansions. + for expansion in expander.expansions() { + if !expansion.rules.contains(&rule.id) { + continue; + } + expansion_graph(expansion, &prog); + } + + Ok(()) +} + +fn expansion_graph(expansion: &Expansion, prog: &Program) { + // Header. + println!("graph {{"); + println!("\tnode [shape=box, fontname=monospace];"); + + // Binding nodes. + let lookup_binding = + |binding_id: BindingId| expansion.bindings[binding_id.index()].clone().unwrap(); + for (i, binding) in expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + println!( + "\tb{i} [label=\"{i}: {}\"];", + binding_string(binding, expansion.term, prog, lookup_binding) + ); + } + } + + // Edges. + for (i, binding) in expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + for source in binding.sources() { + println!("\tb{i} -- b{j};", j = source.index()); + } + } + } + + println!("}}"); +} diff --git a/cranelift/isle/veri/veri/src/bin/reachable.rs b/cranelift/isle/veri/veri/src/bin/reachable.rs new file mode 100644 index 000000000000..254ec0ad30e5 --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/reachable.rs @@ -0,0 +1,72 @@ +use std::collections::HashMap; + +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle_veri::{program::Program, reachability::Reachability}; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, + + /// Whether to disable expansion of internal extractors. + #[arg(long)] + no_expand_internal_extractors: bool, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let prog = Program::from_files(&inputs, !opts.no_expand_internal_extractors)?; + + // Derive rule sets. + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + println!("#term_rule_sets = {}", term_rule_sets.len()); + + // Construct reachability. + let reach = Reachability::build(&term_rule_sets); + + for (term_id, rule_set) in &term_rule_sets { + let cyclic = reach.is_cyclic(*term_id); + let reachable = reach.reachable(*term_id); + + println!("term = {}", prog.term_name(*term_id)); + println!("\tcyclic = {cyclic}"); + println!("\t#rules = {}", rule_set.rules.len()); + println!("\t#reachable = {}", reachable.len()); + for reach_term_id in reachable { + println!("\treachable = {}", prog.term_name(*reach_term_id)); + } + } + + Ok(()) +} diff --git a/cranelift/isle/veri/veri/src/bin/rule_stats.rs b/cranelift/isle/veri/veri/src/bin/rule_stats.rs new file mode 100644 index 000000000000..65ceca0cdd5c --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/rule_stats.rs @@ -0,0 +1,116 @@ +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle::sema::{Pattern, Rule, RuleId, Term}; +use cranelift_isle_veri::program::Program; +use std::collections::HashMap; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let expand_internal_extractors = false; + let prog = Program::from_files(&inputs, expand_internal_extractors)?; + + // Stats. + let rules = prog.rules_by_term(); + let mut total_num_terms = 0; + let mut total_num_rules = 0; + let mut term_class_counts: HashMap = HashMap::new(); + for term in &prog.termenv.terms { + let rule_ids = rules.get(&term.id).cloned().unwrap_or_default(); + let class = classify_term(&prog, term, &rule_ids); + *term_class_counts.entry(class.clone()).or_default() += 1; + + total_num_terms += 1; + total_num_rules += rule_ids.len(); + + println!("{}\t{}\t{}", prog.term_name(term.id), class, rule_ids.len()); + } + + println!(); + println!("TOTAL: num_terms = {total_num_terms}",); + println!("TOTAL: num_rules = {total_num_rules}"); + for (class, count) in term_class_counts { + println!("TOTAL: class:{class} = {count}"); + } + + Ok(()) +} + +fn classify_term(prog: &Program, term: &Term, rule_ids: &[RuleId]) -> String { + if term.is_enum_variant() { + return "enum_variant".to_string(); + } + + if term.has_external_constructor() || term.has_external_extractor() { + return "external".to_string(); + } + + if term.has_extractor() { + return "extractor".to_string(); + } + + assert!(term.has_constructor()); + + if rule_ids.len() == 1 && is_macro_rule(prog.rule(rule_ids[0])) { + return "macro".to_string(); + } + + "constructor".to_string() +} + +fn is_macro_rule(rule: &Rule) -> bool { + if !rule.iflets.is_empty() { + return false; + } + + for arg in &rule.args { + if !is_any_pattern(arg) { + return false; + } + } + + true +} + +fn is_any_pattern(pattern: &Pattern) -> bool { + match pattern { + Pattern::BindPattern(_, _, subpat) => is_any_pattern(subpat), + Pattern::Wildcard(_) => true, + _ => false, + } +} diff --git a/cranelift/isle/veri/veri/src/bin/spec_lines.rs b/cranelift/isle/veri/veri/src/bin/spec_lines.rs new file mode 100644 index 000000000000..f75fdd56672f --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/spec_lines.rs @@ -0,0 +1,77 @@ +use std::sync::Arc; + +use anyhow::{Result, bail, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle::{error::Errors, files::Files, lexer, parser}; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + env_logger::builder() + .format_level(false) + .format_timestamp(None) + .format_target(false) + .init(); + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + + let files = match Files::from_paths(inputs, &[]) { + Ok(files) => files, + Err((path, err)) => { + bail!(Errors::from_io( + err, + format!("cannot read file {}", path.display()), + )) + } + }; + + let files = Arc::new(files); + + let mut defs = Vec::new(); + for (file, src) in files.file_texts.iter().enumerate() { + let lexer = match lexer::Lexer::new(file, src) { + Ok(lexer) => lexer, + Err(err) => bail!(Errors::new(vec![err], files)), + }; + + match parser::parse(lexer) { + Ok(mut ds) => defs.append(&mut ds), + Err(err) => bail!(Errors::new(vec![err], files)), + } + } + + Ok(()) +} diff --git a/cranelift/isle/veri/veri/src/bin/status.rs b/cranelift/isle/veri/veri/src/bin/status.rs new file mode 100644 index 000000000000..7b9bf51e2ca3 --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/status.rs @@ -0,0 +1,181 @@ +use std::collections::{HashMap, HashSet}; + +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle::sema::TermId; +use cranelift_isle_veri::expand::{Chaining, Expander, Expansion}; +use cranelift_isle_veri::program::Program; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, + + /// Tag defining in-scope expansions. + #[arg(long = "include-tag", value_name = "TAG")] + include_tag: String, + + /// Skip expansions containing terms with this tag. + #[arg(long = "skip-tag", value_name = "TAG")] + skip_tags: Vec, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let root_term = if opts.name != "opt" { + "lower" + } else { + "simplify" + }; + let expand_internal_extractors = false; + let prog = Program::from_files(&inputs, expand_internal_extractors)?; + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + + // Generate expansions. + let chaining = Chaining::new(&prog, &term_rule_sets)?; + let mut expander = Expander::new(&prog, &term_rule_sets, chaining); + expander.add_root_term_name(root_term)?; + expander.set_prune_infeasible(true); + expander.expand(); + + // Show status. + status( + expander.expansions(), + opts.include_tag, + &opts.skip_tags, + &prog, + ); + + Ok(()) +} + +fn status(expansions: &Vec, include_tag: String, skip_tags: &[String], prog: &Program) { + // Report config + println!("CONFIG"); + println!("include_tag\t{include_tag}"); + println!("skip_tags\t{skip_tags}", skip_tags = skip_tags.join(",")); + println!(); + + // Collect status + let mut total = 0usize; + let mut num_out_of_scope = 0usize; + let mut num_specified = 0usize; + let mut term_unspecified_counts: HashMap = HashMap::new(); + let mut internal_constructors = HashSet::new(); + for expansion in expansions { + total += 1; + + if !expansion_in_scope(expansion, &include_tag, skip_tags, prog) { + num_out_of_scope += 1; + continue; + } + + let unspecified = unspecified_terms(expansion, prog); + if unspecified.is_empty() { + num_specified += 1; + } + for term_id in unspecified { + *term_unspecified_counts.entry(term_id).or_default() += 1; + } + + internal_constructors.extend(expansion_internal_constructors(expansion, prog)); + } + + // Summary + println!("SUMMARY"); + + let num_in_scope = total - num_out_of_scope; + let coverage = (num_specified as f64 / num_in_scope as f64) * 100.0; + + println!("total\t{total}"); + println!("out_of_scope\t{num_out_of_scope}"); + println!("in_scope\t{num_in_scope}"); + println!("specified\t{num_specified}"); + println!("coverage\t{coverage:.2}"); + println!(); + + // Unspecified terms + println!("UNSPECIFIED"); + + let mut term_unspecified_counts: Vec<_> = term_unspecified_counts.into_iter().collect(); + term_unspecified_counts.sort_by_key(|(_, count)| -*count); + for (term_id, count) in term_unspecified_counts { + println!("{term}\t{count}", term = prog.term_name(term_id)); + } + + // Internal constructors + println!(); + println!("INTERNAL CONSTRUCTORS"); + for term_id in internal_constructors { + println!("{term}", term = prog.term_name(term_id)); + } +} + +fn expansion_in_scope( + expansion: &Expansion, + include_tag: &String, + skip_tags: &[String], + prog: &Program, +) -> bool { + let tags = expansion.tags(prog); + if !tags.contains(include_tag) { + return false; + } + for tag in skip_tags { + if tags.contains(tag) { + return false; + } + } + true +} + +fn unspecified_terms(expansion: &Expansion, prog: &Program) -> Vec { + expansion + .terms(prog) + .iter() + .copied() + .filter(|term_id| !prog.specenv.has_spec(*term_id)) + .collect() +} + +fn expansion_internal_constructors(expansion: &Expansion, prog: &Program) -> Vec { + expansion + .terms(prog) + .iter() + .copied() + .filter(|term_id| { + let term = prog.term(*term_id); + term.has_internal_constructor() + }) + .collect() +} diff --git a/cranelift/isle/veri/veri/src/bin/trie_dump.rs b/cranelift/isle/veri/veri/src/bin/trie_dump.rs new file mode 100644 index 000000000000..e1ab3cd140bb --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/trie_dump.rs @@ -0,0 +1,56 @@ +use anyhow::{Result, format_err}; +use clap::Parser; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle_veri::{debug::print_rule_set, program::Program}; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, required = true)] + name: String, + + /// Path to codegen crate directory. + #[arg(long, required = true)] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. + #[arg(long, required = true)] + work_dir: std::path::PathBuf, + + /// Whether to disable expansion of internal extractors. + #[arg(long)] + no_expand_internal_extractors: bool, +} + +impl Opts { + fn isle_input_files(&self) -> Result> { + // Generate ISLE files. + let gen_dir = &self.work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + let opts = Opts::parse(); + + // Read ISLE inputs. + let inputs = opts.isle_input_files()?; + let prog = Program::from_files(&inputs, !opts.no_expand_internal_extractors)?; + + // Dump rule sets. + let term_rule_sets = prog.build_trie()?; + for (term_id, rule_set) in &term_rule_sets { + print_rule_set(&prog, term_id, rule_set); + } + + Ok(()) +} diff --git a/cranelift/isle/veri/veri/src/bin/veri.rs b/cranelift/isle/veri/veri/src/bin/veri.rs new file mode 100644 index 000000000000..f29fe918dda0 --- /dev/null +++ b/cranelift/isle/veri/veri/src/bin/veri.rs @@ -0,0 +1,213 @@ +use std::time::Duration; + +use anyhow::{Result, format_err}; +use clap::{ArgAction, Parser}; +use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; +use cranelift_isle_veri::runner::{Filter, Runner, SolverBackend, SolverRule}; + +#[derive(Parser)] +struct Opts { + /// Name of the ISLE compilation. + #[arg(long, default_value = "aarch64")] + name: String, + + /// Path to codegen crate directory. + #[arg(long, default_value = "cranelift/codegen")] + codegen_crate_dir: std::path::PathBuf, + + /// Working directory. Defaults to a fresh temporary directory. + #[arg(long)] + work_dir: Option, + + /// Filter expansions. + #[arg(long = "filter", value_name = "FILTER")] + filters: Vec, + + /// Exclude a default set of tags that are not yet well supported: + /// `vector`, `atomics`, `spectre`, `narrowfloat`, `amode_const`, and `i128`. + #[arg(long)] + default_excludes: bool, + + /// Only expand from the given root term, instead of all terms with rules. + #[arg(long = "only-root", value_name = "TERM")] + only_root: Option, + + /// Don't skip expansions tagged TODO. + #[arg(long = "no-skip-todo", action = ArgAction::SetFalse)] + skip_todo: bool, + + /// Solver backend to use. + #[arg(long = "solver", default_value = "cvc5", env = "ISLE_VERI_SOLVER")] + solver_backend: SolverBackend, + + /// Solver selection rule of the form `=`. Earlier rules take precedence. + #[arg(long = "solver-rule")] + solver_rules: Vec, + + /// Ignore explicit solver selection tags `solver_`. + #[arg(long)] + ignore_solver_tags: bool, + + /// Per-query timeout, in seconds. + #[arg(long, default_value = "30", env = "ISLE_VERI_TIMEOUT")] + timeout: u64, + + /// Number of threads to use. + #[arg(long, default_value = "1")] + num_threads: usize, + + /// Log directory. + #[arg(long)] + log_dir: Option, + + /// Write results to files under log directory. (Use 0 to select automatically.) + #[arg(long)] + results_to_log_dir: bool, + + /// Skip solver. + #[arg(long, env = "ISLE_VERI_SKIP_SOLVER")] + skip_solver: bool, + + /// Dump debug output. + #[arg(long)] + debug: bool, +} + +impl Opts { + fn isle_input_files(&self, work_dir: &std::path::Path) -> Result> { + // Generate ISLE files. + let gen_dir = work_dir; + generate_isle(gen_dir)?; + + // Lookup ISLE compilations. + let compilations = get_isle_compilations(&self.codegen_crate_dir, gen_dir); + + // Return inputs from the matching compilation, if any. + Ok(compilations + .lookup(&self.name) + .ok_or(format_err!("unknown ISLE compilation: {}", self.name))? + .paths()?) + } +} + +fn main() -> Result<()> { + env_logger::builder().format_target(false).init(); + let opts = Opts::parse(); + + // Setup thread pool. + rayon::ThreadPoolBuilder::new() + .num_threads(opts.num_threads) + .build_global()?; + log::info!("num theads: {}", rayon::current_num_threads()); + + // Resolve the working directory, defaulting to a fresh temporary directory + // that lives for the duration of the run. + let temp_dir = match &opts.work_dir { + Some(_) => None, + None => Some(tempfile::tempdir()?), + }; + let work_dir: &std::path::Path = match (&opts.work_dir, &temp_dir) { + (Some(dir), _) => dir, + (None, Some(temp)) => temp.path(), + (None, None) => unreachable!(), + }; + + // Read ISLE inputs. + let inputs = opts.isle_input_files(work_dir)?; + let mut runner = Runner::from_files(&inputs)?; + + // Scope expansion to a single root term, if requested. Otherwise the + // default is to expand from every term that has rules. + if let Some(root) = &opts.only_root { + runner.set_root_term(root); + } + + // Configure runner. + // Default behaviour is to include every expansion (all paths from all + // roots); any provided filters only narrow that down via `exclude`. + let mut filters = opts.filters.clone(); + let default_exclude_tags: &[&str] = &[ + "vector", + "atomics", + "spectre", + "narrowfloat", + "amode_const", + "i128", + "slow", + ]; + if opts.default_excludes { + for tag in default_exclude_tags { + filters.push(format!("exclude:tag:{tag}").parse()?); + } + } + runner.filters(&filters); + if opts.skip_todo { + runner.skip_tag("TODO"); + } + + runner.set_default_solver_backend(opts.solver_backend); + if !opts.ignore_solver_tags { + runner.add_solver_tag_rules(); + } + for solver_rule in opts.solver_rules { + runner.add_solver_rule(solver_rule); + } + + runner.set_timeout(Duration::from_secs(opts.timeout)); + // Effective log directory: the runner defaults to `.veriisle` unless + // overridden here. + let log_dir = opts + .log_dir + .clone() + .unwrap_or_else(|| std::path::PathBuf::from(".veriisle")); + if let Some(log_dir) = opts.log_dir { + runner.set_log_dir(log_dir); + } + runner.set_results_to_log_dir(opts.results_to_log_dir); + runner.skip_solver(opts.skip_solver); + runner.debug(opts.debug); + + // Summarize what is being excluded and where output is going before + // starting verification. + println!("=== veri configuration ==="); + println!("working directory: {}", work_dir.display()); + println!("log directory: {}", log_dir.display()); + println!( + "results to log dir: {}", + if opts.results_to_log_dir { + format!("yes (results.out under {})", log_dir.display()) + } else { + "no (results printed to stdout)".to_string() + } + ); + if let Some(root) = &opts.only_root { + println!("only root term: {root}"); + } + if opts.default_excludes { + println!( + "Excluding ISLE terms with any of the following tags (the default exclude set): {}.", + default_exclude_tags.join(", ") + ); + } else { + println!( + "Not applying any default tag exclusions (pass --default-excludes to skip the tags that are not yet well supported)." + ); + } + if opts.skip_todo { + println!("Excluding ISLE terms tagged TODO."); + } else { + println!("Including ISLE terms tagged TODO."); + } + if opts.filters.is_empty() { + println!("Not applying any additional filters."); + } else { + println!("Also applying the following filters:"); + for filter in &opts.filters { + println!(" - {}", filter.describe()); + } + } + println!("=========================="); + + // Run. + runner.run() +} diff --git a/cranelift/isle/veri/veri/src/debug.rs b/cranelift/isle/veri/veri/src/debug.rs new file mode 100644 index 000000000000..f5c3a00ac451 --- /dev/null +++ b/cranelift/isle/veri/veri/src/debug.rs @@ -0,0 +1,343 @@ +use std::io::{self, Write}; + +use crate::{ + expand::{Constrain, Expansion}, + program::Program, + trie::{BindingType, binding_type}, + types::field_name_by_index, +}; +use cranelift_isle::{ + sema::{TermId, Type, TypeEnv}, + trie_again::{Binding, BindingId, Constraint, RuleSet}, +}; + +pub fn print_expansion(prog: &Program, expansion: &Expansion) { + write_expansion(&mut io::stdout(), prog, expansion).expect("write to stdout failed"); +} + +pub fn write_expansion( + out: &mut dyn Write, + prog: &Program, + expansion: &Expansion, +) -> io::Result<()> { + writeln!(out, "expansion {{")?; + + // Term. + writeln!(out, "\tterm = {}", prog.term_name(expansion.term))?; + + // Rules. + writeln!(out, "\trules = [")?; + for rule_id in &expansion.rules { + let rule = &prog.termenv.rules[rule_id.index()]; + writeln!(out, "\t\t{}", rule.identifier(&prog.tyenv, &prog.files))?; + } + writeln!(out, "\t]")?; + + // Negated rules. + writeln!(out, "\tnegated = [")?; + for rule_id in &expansion.negated { + let rule = &prog.termenv.rules[rule_id.index()]; + writeln!(out, "\t\t{}", rule.identifier(&prog.tyenv, &prog.files))?; + } + writeln!(out, "\t]")?; + + // Bindings. + let lookup_binding = + |binding_id: BindingId| expansion.bindings[binding_id.index()].clone().unwrap(); + writeln!(out, "\tbindings = [")?; + for (i, binding) in expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + let ty = binding_type(binding, expansion.term, prog, lookup_binding); + writeln!( + out, + "\t\t{i}: {}\t{}", + ty.display(&prog.tyenv), + binding_string(binding, expansion.term, prog, lookup_binding), + )?; + } + } + writeln!(out, "\t]")?; + + // Constraints. + writeln!(out, "\tconstraints = [")?; + for constrain in &expansion.constraints { + writeln!(out, "\t\t{}", constrain_string(constrain, &prog.tyenv))?; + } + writeln!(out, "\t]")?; + + // Equals. + if !expansion.equals.is_empty() { + writeln!(out, "\tequals = [")?; + for (left, right) in expansion.equalities() { + writeln!(out, "\t\t{} == {}", left.index(), right.index())?; + } + writeln!(out, "\t]")?; + } + + // Parameters. + writeln!(out, "\tparameters = [")?; + for binding_id in &expansion.parameters { + writeln!(out, "\t\t{}", binding_id.index())?; + } + writeln!(out, "\t]")?; + + // Result. + writeln!(out, "\tresult = {}", expansion.result.index())?; + + // Feasibility. + writeln!(out, "\tfeasible = {}", expansion.is_feasible())?; + + writeln!(out, "}}")?; + Ok(()) +} + +pub fn print_rule_set(prog: &Program, term_id: &TermId, rule_set: &RuleSet) { + println!("term {{"); + println!("\tname = {}", prog.term_name(*term_id)); + + // Bindings. + let lookup_binding = |binding_id: BindingId| rule_set.bindings[binding_id.index()].clone(); + println!("\tbindings = ["); + for (i, binding) in rule_set.bindings.iter().enumerate() { + let ty = binding_type(binding, *term_id, prog, lookup_binding); + println!( + "\t\t{i}: {}\t{}", + ty.display(&prog.tyenv), + binding_string(binding, *term_id, prog, lookup_binding), + ); + } + println!("\t]"); + + // Rules. + println!("\trules = ["); + for rule in &rule_set.rules { + assert_eq!(rule.iterators.len(), 0); + println!("\t\t{{"); + println!("\t\t\tpos = {}", rule.pos.pretty_print_line(&prog.files)); + println!("\t\t\tconstraints = ["); + for i in 0..rule_set.bindings.len() { + if let Some(constraint) = rule.get_constraint(i.try_into().unwrap()) { + println!( + "\t\t\t\t{}:\t{}", + i, + constraint_string(&constraint, &prog.tyenv) + ); + } + } + println!("\t\t\t]"); + if !rule.equals.is_empty() { + println!("\t\t\tequals = ["); + for i in 0..rule_set.bindings.len() { + let binding_id = i.try_into().unwrap(); + if let Some(eq) = rule.equals.find(binding_id) + && eq != binding_id + { + println!("\t\t\t\t{} == {}", binding_id.index(), eq.index()); + } + } + println!("\t\t\t]"); + } + println!("\t\t\tprio = {}", rule.prio); + println!("\t\t\tresult = {}", rule.result.index()); + if !rule.impure.is_empty() { + println!( + "\t\t\timpure = {impure:?}", + impure = rule + .impure + .iter() + .copied() + .map(BindingId::index) + .collect::>() + ); + } + println!("\t\t}}"); + } + println!("\t]"); + + println!("}}"); +} + +pub fn binding_string( + binding: &Binding, + term_id: TermId, + prog: &Program, + lookup_binding: impl Fn(BindingId) -> Binding, +) -> String { + match binding { + Binding::Argument { index } => format!("argument({})", index.index()), + Binding::ConstInt { val, ty } => { + let ty = &prog.tyenv.types[ty.index()]; + format!("const_int({val}, {name})", name = ty.name(&prog.tyenv)) + } + Binding::ConstBool { val, ty } => { + let ty = &prog.tyenv.types[ty.index()]; + format!("const_bool({val}, {name})", name = ty.name(&prog.tyenv)) + } + Binding::ConstPrim { val } => format!("const_prim({})", prog.tyenv.syms[val.index()]), + Binding::Constructor { + term, + parameters, + instance, + } => { + let name = prog.term_name(*term); + format!( + "constructor({name}, {parameters:?}, {instance})", + parameters = parameters + .iter() + .copied() + .map(BindingId::index) + .collect::>() + ) + } + Binding::Extractor { term, parameter } => { + let name = prog.term_name(*term); + format!( + "extractor({name}, {parameter})", + parameter = parameter.index() + ) + } + Binding::MatchVariant { + source, + variant, + field, + } => { + let source_binding = lookup_binding(*source); + let source_type = binding_type(&source_binding, term_id, prog, lookup_binding); + let BindingType::Base(source_type_id) = source_type else { + unreachable!("source of match variant should be a base type") + }; + + // Lookup variant. + let enum_ty = &prog.tyenv.types[source_type_id.index()]; + let enum_name = enum_ty.name(&prog.tyenv); + let variant = match enum_ty { + Type::Enum { variants, .. } => &variants[variant.index()], + _ => unreachable!("source match variant should be an enum"), + }; + let variant_name = &prog.tyenv.syms[variant.name.index()]; + + // Field. + let field_name = field_name_by_index(&variant.fields, field.index(), &prog.tyenv); + + format!( + "match_variant({source}, {enum_name}::{variant_name}, {field_name})", + source = source.index(), + ) + } + Binding::MakeVariant { + ty, + variant, + fields, + } => { + let ty = &prog.tyenv.types[ty.index()]; + let Type::Enum { variants, .. } = ty else { + unreachable!("source match variant should be an enum") + }; + let variant = &variants[variant.index()]; + let variant_name = &prog.tyenv.syms[variant.name.index()]; + format!( + "make_variant({ty}::{variant_name}, {fields:?})", + ty = ty.name(&prog.tyenv), + fields = fields + .iter() + .copied() + .map(BindingId::index) + .collect::>() + ) + } + Binding::MakeStruct { ty, fields } => { + let ty = &prog.tyenv.types[ty.index()]; + let Type::Struct { .. } = ty else { + unreachable!("MakeStruct target should be a struct type") + }; + format!( + "make_struct({ty}, {fields:?})", + ty = ty.name(&prog.tyenv), + fields = fields + .iter() + .copied() + .map(BindingId::index) + .collect::>() + ) + } + Binding::ExtractStruct { source, field } => { + let source_binding = lookup_binding(*source); + let source_type = binding_type(&source_binding, term_id, prog, lookup_binding); + let BindingType::Base(source_type_id) = source_type else { + unreachable!("source of extract_struct should be a base type") + }; + let struct_ty = &prog.tyenv.types[source_type_id.index()]; + let struct_name = struct_ty.name(&prog.tyenv); + let fields = match struct_ty { + Type::Struct { fields, .. } => fields, + _ => unreachable!("source of extract_struct should be a struct"), + }; + let field_name = field_name_by_index(fields, field.index(), &prog.tyenv); + format!( + "extract_struct({source}, {struct_name}, {field_name})", + source = source.index(), + ) + } + Binding::MakeSome { inner } => format!("some({inner})", inner = inner.index()), + Binding::MatchSome { source } => format!("match_some({source})", source = source.index()), + Binding::MatchTuple { source, field } => format!( + "match_tuple({source}, {field})", + source = source.index(), + field = field.index() + ), + Binding::Iterator { .. } => unimplemented!("iterator bindings unsupported"), + } +} + +pub fn constrain_string(constrain: &Constrain, tyenv: &TypeEnv) -> String { + match constrain { + Constrain::Match(binding_id, constraint) => format!( + "{}: {}", + binding_id.index(), + constraint_string(constraint, tyenv) + ), + Constrain::NotAll(constraints) => { + format!( + "not_all({constraints})", + constraints = constraints + .iter() + .map(|c| constrain_string(c, tyenv)) + .collect::>() + .join(", "), + ) + } + } +} + +pub fn constraint_string(constraint: &Constraint, tyenv: &TypeEnv) -> String { + match constraint { + Constraint::Variant { ty, variant, .. } => { + let ty = &tyenv.types[ty.index()]; + match ty { + Type::Primitive(_, sym, _) => { + format!("variant({})", tyenv.syms[sym.index()].clone()) + } + Type::Enum { name, variants, .. } => { + let name = &tyenv.syms[name.index()]; + let variant = &variants[variant.index()]; + let variant_name = &tyenv.syms[variant.name.index()]; + format!("variant({name}::{variant_name})") + } + Type::Builtin(b) => { + format!("variant({})", b.name()) + } + Type::Struct { .. } => { + unreachable!("variant constraint should not apply to a struct type") + } + } + } + Constraint::Struct { ty, .. } => { + let ty = &tyenv.types[ty.index()]; + format!("struct({})", ty.name(tyenv)) + } + Constraint::ConstInt { val, .. } => format!("const_int({val})"), + Constraint::ConstBool { val, .. } => format!("const_bool({val})"), + Constraint::ConstPrim { val } => format!("const_prim({})", tyenv.syms[val.index()]), + Constraint::Some => "some".to_string(), + } +} diff --git a/cranelift/isle/veri/veri/src/encoded/cls.rs b/cranelift/isle/veri/veri/src/encoded/cls.rs new file mode 100644 index 000000000000..5dd89e6e81e0 --- /dev/null +++ b/cranelift/isle/veri/veri/src/encoded/cls.rs @@ -0,0 +1,2384 @@ +// Adapted from https://stackoverflow.com/questions/23856596/how-to-count-leading-zeros-in-a-32-bit-unsigned-integer +use easy_smt::*; + +fn declare(smt: &mut Context, name: String, val: SExpr) -> SExpr { + smt.declare_const(name.clone(), val).unwrap(); + smt.atom(name) +} + +pub fn cls64(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + // Generated code. + // total zeros counter + let zret0 = declare( + smt, + format!("zret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + )); + // round 1 + let zret1 = declare( + smt, + format!("zret1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zy32 = declare( + smt, + format!("zy32_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zx32 = declare( + smt, + format!("zx32_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(zy32, smt.bvlshr(x, smt.atom("#x0000000000000020")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy32, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zret1, zret0), + smt.eq( + zret1, + smt.list(vec![ + smt.atom("bvadd"), + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv32"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy32, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zx32, zy32), + smt.eq(zx32, x), + ])); + // round 2 + let zret2 = declare( + smt, + format!("zret2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zy16 = declare( + smt, + format!("zy16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zx16 = declare( + smt, + format!("zx16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(zy16, smt.bvlshr(zx32, smt.atom("#x0000000000000010")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zret2, zret1), + smt.eq( + zret2, + smt.list(vec![ + smt.atom("bvadd"), + zret1, + smt.list(vec![smt.atoms().und, smt.atom("bv16"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zx16, zy16), + smt.eq(zx16, zx32), + ])); + // round 3 + let zret3 = declare( + smt, + format!("zret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zy8 = declare( + smt, + format!("zy8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zx8 = declare( + smt, + format!("zx8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(zy8, smt.bvlshr(zx16, smt.atom("#x0000000000000008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zret3, zret2), + smt.eq( + zret3, + smt.list(vec![ + smt.atom("bvadd"), + zret2, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zx8, zy8), + smt.eq(zx8, zx16), + ])); + // round 4 + let zret4 = declare( + smt, + format!("zret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zy4 = declare( + smt, + format!("zy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zx4 = declare( + smt, + format!("zx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(zy4, smt.bvlshr(zx8, smt.atom("#x0000000000000004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zret4, zret3), + smt.eq( + zret4, + smt.list(vec![ + smt.atom("bvadd"), + zret3, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zx4, zy4), + smt.eq(zx4, zx8), + ])); + // round 5 + let zret5 = declare( + smt, + format!("zret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zy2 = declare( + smt, + format!("zy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zx2 = declare( + smt, + format!("zx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(zy2, smt.bvlshr(zx4, smt.atom("#x0000000000000002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zret5, zret4), + smt.eq( + zret5, + smt.list(vec![ + smt.atom("bvadd"), + zret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zx2, zy2), + smt.eq(zx2, zx4), + ])); + // round 6 + let zret6 = declare( + smt, + format!("zret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zy1 = declare( + smt, + format!("zy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let zx1 = declare( + smt, + format!("zx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(zy1, smt.bvlshr(zx2, smt.atom("#x0000000000000001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zret6, zret5), + smt.eq( + zret6, + smt.list(vec![ + smt.atom("bvadd"), + zret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zx1, zy1), + smt.eq(zx1, zx2), + ])); + // last round + let zret7 = declare( + smt, + format!("zret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zx1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(zret7, zret6), + smt.eq( + zret7, + smt.list(vec![ + smt.atom("bvadd"), + zret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + let clzret = declare( + smt, + format!("clzret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + smt.eq(clzret, zret7), + smt.eq( + clzret, + smt.list(vec![ + smt.atom("bvsub"), + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + // total zeros counter + let sret0 = declare( + smt, + format!("sret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + )); + // round 1 + let sret1 = declare( + smt, + format!("sret1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sy32 = declare( + smt, + format!("sy32_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sx32 = declare( + smt, + format!("sx32_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(sy32, smt.bvashr(x, smt.atom("#x0000000000000020")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy32, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sret1, sret0), + smt.eq( + sret1, + smt.list(vec![ + smt.atom("bvadd"), + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv32"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy32, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sx32, sy32), + smt.eq(sx32, x), + ])); + // round 2 + let sret2 = declare( + smt, + format!("sret2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sy16 = declare( + smt, + format!("sy16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sx16 = declare( + smt, + format!("sx16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(sy16, smt.bvashr(sx32, smt.atom("#x0000000000000010")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy16, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sret2, sret1), + smt.eq( + sret2, + smt.list(vec![ + smt.atom("bvadd"), + sret1, + smt.list(vec![smt.atoms().und, smt.atom("bv16"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy16, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sx16, sy16), + smt.eq(sx16, sx32), + ])); + // round 3 + let sret3 = declare( + smt, + format!("sret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sy8 = declare( + smt, + format!("sy8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sx8 = declare( + smt, + format!("sx8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(sy8, smt.bvashr(sx16, smt.atom("#x0000000000000008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy8, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sret3, sret2), + smt.eq( + sret3, + smt.list(vec![ + smt.atom("bvadd"), + sret2, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy8, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sx8, sy8), + smt.eq(sx8, sx16), + ])); + // round 4 + let sret4 = declare( + smt, + format!("sret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sy4 = declare( + smt, + format!("sy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sx4 = declare( + smt, + format!("sx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(sy4, smt.bvashr(sx8, smt.atom("#x0000000000000004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sret4, sret3), + smt.eq( + sret4, + smt.list(vec![ + smt.atom("bvadd"), + sret3, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sx4, sy4), + smt.eq(sx4, sx8), + ])); + // round 5 + let sret5 = declare( + smt, + format!("sret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sy2 = declare( + smt, + format!("sy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sx2 = declare( + smt, + format!("sx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(sy2, smt.bvashr(sx4, smt.atom("#x0000000000000002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sret5, sret4), + smt.eq( + sret5, + smt.list(vec![ + smt.atom("bvadd"), + sret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sx2, sy2), + smt.eq(sx2, sx4), + ])); + // round 6 + let sret6 = declare( + smt, + format!("sret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sy1 = declare( + smt, + format!("sy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let sx1 = declare( + smt, + format!("sx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(sy1, smt.bvashr(sx2, smt.atom("#x0000000000000001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sret6, sret5), + smt.eq( + sret6, + smt.list(vec![ + smt.atom("bvadd"), + sret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sx1, sy1), + smt.eq(sx1, sx2), + ])); + // last round + let sret7 = declare( + smt, + format!("sret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sx1, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv18446744073709551615"), + smt.numeral(64), + ]), + ), + ]), + smt.eq(sret7, sret6), + smt.eq( + sret7, + smt.list(vec![ + smt.atom("bvadd"), + sret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + let clsret = declare( + smt, + format!("clsret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + smt.eq(clsret, sret7), + smt.eq( + clsret, + smt.list(vec![ + smt.atom("bvsub"), + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + let cls64ret = declare( + smt, + format!("cls64ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("bvsle"), + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + x, + ]), + smt.eq(cls64ret, clzret), + smt.eq(cls64ret, clsret), + ])); + + cls64ret +} + +pub fn cls32(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(31, 0, x); + + // Generated code. + // total zeros counter + let zret0 = declare( + smt, + format!("zret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + )); + // round 1 + let zret2 = declare( + smt, + format!("zret2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zy16 = declare( + smt, + format!("zy16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zx16 = declare( + smt, + format!("zx16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(zy16, smt.bvlshr(x, smt.atom("#x00000010")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zret2, zret0), + smt.eq( + zret2, + smt.list(vec![ + smt.atom("bvadd"), + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv16"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zx16, zy16), + smt.eq(zx16, x), + ])); + // round 2 + let zret3 = declare( + smt, + format!("zret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zy8 = declare( + smt, + format!("zy8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zx8 = declare( + smt, + format!("zx8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(zy8, smt.bvlshr(zx16, smt.atom("#x00000008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zret3, zret2), + smt.eq( + zret3, + smt.list(vec![ + smt.atom("bvadd"), + zret2, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zx8, zy8), + smt.eq(zx8, zx16), + ])); + // round 3 + let zret4 = declare( + smt, + format!("zret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zy4 = declare( + smt, + format!("zy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zx4 = declare( + smt, + format!("zx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(zy4, smt.bvlshr(zx8, smt.atom("#x00000004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zret4, zret3), + smt.eq( + zret4, + smt.list(vec![ + smt.atom("bvadd"), + zret3, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zx4, zy4), + smt.eq(zx4, zx8), + ])); + // round 4 + let zret5 = declare( + smt, + format!("zret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zy2 = declare( + smt, + format!("zy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zx2 = declare( + smt, + format!("zx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(zy2, smt.bvlshr(zx4, smt.atom("#x00000002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zret5, zret4), + smt.eq( + zret5, + smt.list(vec![ + smt.atom("bvadd"), + zret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zx2, zy2), + smt.eq(zx2, zx4), + ])); + // round 5 + let zret6 = declare( + smt, + format!("zret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zy1 = declare( + smt, + format!("zy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let zx1 = declare( + smt, + format!("zx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(zy1, smt.bvlshr(zx2, smt.atom("#x00000001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zret6, zret5), + smt.eq( + zret6, + smt.list(vec![ + smt.atom("bvadd"), + zret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zx1, zy1), + smt.eq(zx1, zx2), + ])); + // last round + let zret7 = declare( + smt, + format!("zret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zx1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(zret7, zret6), + smt.eq( + zret7, + smt.list(vec![ + smt.atom("bvadd"), + zret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + let clzret = declare( + smt, + format!("clzret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + smt.eq(clzret, zret7), + smt.eq( + clzret, + smt.list(vec![ + smt.atom("bvsub"), + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + // total zeros counter + let sret0 = declare( + smt, + format!("sret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + )); + // round 1 + let sret2 = declare( + smt, + format!("sret2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sy16 = declare( + smt, + format!("sy16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sx16 = declare( + smt, + format!("sx16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(sy16, smt.bvashr(x, smt.atom("#x00000010")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy16, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sret2, sret0), + smt.eq( + sret2, + smt.list(vec![ + smt.atom("bvadd"), + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv16"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy16, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sx16, sy16), + smt.eq(sx16, x), + ])); + // round 2 + let sret3 = declare( + smt, + format!("sret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sy8 = declare( + smt, + format!("sy8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sx8 = declare( + smt, + format!("sx8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(sy8, smt.bvashr(sx16, smt.atom("#x00000008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy8, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sret3, sret2), + smt.eq( + sret3, + smt.list(vec![ + smt.atom("bvadd"), + sret2, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy8, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sx8, sy8), + smt.eq(sx8, sx16), + ])); + // round 3 + let sret4 = declare( + smt, + format!("sret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sy4 = declare( + smt, + format!("sy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sx4 = declare( + smt, + format!("sx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(sy4, smt.bvashr(sx8, smt.atom("#x00000004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sret4, sret3), + smt.eq( + sret4, + smt.list(vec![ + smt.atom("bvadd"), + sret3, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sx4, sy4), + smt.eq(sx4, sx8), + ])); + // round 4 + let sret5 = declare( + smt, + format!("sret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sy2 = declare( + smt, + format!("sy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sx2 = declare( + smt, + format!("sx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(sy2, smt.bvashr(sx4, smt.atom("#x00000002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sret5, sret4), + smt.eq( + sret5, + smt.list(vec![ + smt.atom("bvadd"), + sret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sx2, sy2), + smt.eq(sx2, sx4), + ])); + // round 5 + let sret6 = declare( + smt, + format!("sret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sy1 = declare( + smt, + format!("sy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let sx1 = declare( + smt, + format!("sx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(sy1, smt.bvashr(sx2, smt.atom("#x00000001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sret6, sret5), + smt.eq( + sret6, + smt.list(vec![ + smt.atom("bvadd"), + sret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sx1, sy1), + smt.eq(sx1, sx2), + ])); + // last round + let sret7 = declare( + smt, + format!("sret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sx1, + smt.list(vec![ + smt.atoms().und, + smt.atom("bv4294967295"), + smt.numeral(32), + ]), + ), + ]), + smt.eq(sret7, sret6), + smt.eq( + sret7, + smt.list(vec![ + smt.atom("bvadd"), + sret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + let clsret = declare( + smt, + format!("clsret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + smt.eq(clsret, sret7), + smt.eq( + clsret, + smt.list(vec![ + smt.atom("bvsub"), + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + let cls32ret = declare( + smt, + format!("cls32ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("bvsle"), + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + x, + ]), + smt.eq(cls32ret, clzret), + smt.eq(cls32ret, clsret), + ])); + + cls32ret +} + +pub fn cls16(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(15, 0, x); + + // Generated code. + // total zeros counter + let zret0 = declare( + smt, + format!("zret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq( + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + )); + // round 1 + let zret3 = declare( + smt, + format!("zret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zy8 = declare( + smt, + format!("zy8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zx8 = declare( + smt, + format!("zx8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(zy8, smt.bvlshr(x, smt.atom("#x0008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zret3, zret0), + smt.eq( + zret3, + smt.list(vec![ + smt.atom("bvadd"), + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zx8, zy8), + smt.eq(zx8, x), + ])); + // round 2 + let zret4 = declare( + smt, + format!("zret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zy4 = declare( + smt, + format!("zy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zx4 = declare( + smt, + format!("zx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(zy4, smt.bvlshr(zx8, smt.atom("#x0004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zret4, zret3), + smt.eq( + zret4, + smt.list(vec![ + smt.atom("bvadd"), + zret3, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zx4, zy4), + smt.eq(zx4, zx8), + ])); + // round 3 + let zret5 = declare( + smt, + format!("zret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zy2 = declare( + smt, + format!("zy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zx2 = declare( + smt, + format!("zx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(zy2, smt.bvlshr(zx4, smt.atom("#x0002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zret5, zret4), + smt.eq( + zret5, + smt.list(vec![ + smt.atom("bvadd"), + zret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zx2, zy2), + smt.eq(zx2, zx4), + ])); + // round 4 + let zret6 = declare( + smt, + format!("zret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zy1 = declare( + smt, + format!("zy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let zx1 = declare( + smt, + format!("zx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(zy1, smt.bvlshr(zx2, smt.atom("#x0001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zret6, zret5), + smt.eq( + zret6, + smt.list(vec![ + smt.atom("bvadd"), + zret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zx1, zy1), + smt.eq(zx1, zx2), + ])); + // last round + let zret7 = declare( + smt, + format!("zret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zx1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(zret7, zret6), + smt.eq( + zret7, + smt.list(vec![ + smt.atom("bvadd"), + zret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + let clzret = declare( + smt, + format!("clzret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + smt.eq(clzret, zret7), + smt.eq( + clzret, + smt.list(vec![ + smt.atom("bvsub"), + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + // total zeros counter + let sret0 = declare( + smt, + format!("sret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq( + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + )); + // round 1 + let sret3 = declare( + smt, + format!("sret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sy8 = declare( + smt, + format!("sy8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sx8 = declare( + smt, + format!("sx8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(sy8, smt.bvashr(x, smt.atom("#x0008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy8, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sret3, sret0), + smt.eq( + sret3, + smt.list(vec![ + smt.atom("bvadd"), + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy8, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sx8, sy8), + smt.eq(sx8, x), + ])); + // round 2 + let sret4 = declare( + smt, + format!("sret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sy4 = declare( + smt, + format!("sy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sx4 = declare( + smt, + format!("sx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(sy4, smt.bvashr(sx8, smt.atom("#x0004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sret4, sret3), + smt.eq( + sret4, + smt.list(vec![ + smt.atom("bvadd"), + sret3, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sx4, sy4), + smt.eq(sx4, sx8), + ])); + // round 3 + let sret5 = declare( + smt, + format!("sret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sy2 = declare( + smt, + format!("sy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sx2 = declare( + smt, + format!("sx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(sy2, smt.bvashr(sx4, smt.atom("#x0002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sret5, sret4), + smt.eq( + sret5, + smt.list(vec![ + smt.atom("bvadd"), + sret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sx2, sy2), + smt.eq(sx2, sx4), + ])); + // round 4 + let sret6 = declare( + smt, + format!("sret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sy1 = declare( + smt, + format!("sy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let sx1 = declare( + smt, + format!("sx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(sy1, smt.bvashr(sx2, smt.atom("#x0001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sret6, sret5), + smt.eq( + sret6, + smt.list(vec![ + smt.atom("bvadd"), + sret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sx1, sy1), + smt.eq(sx1, sx2), + ])); + // last round + let sret7 = declare( + smt, + format!("sret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sx1, + smt.list(vec![smt.atoms().und, smt.atom("bv65535"), smt.numeral(16)]), + ), + ]), + smt.eq(sret7, sret6), + smt.eq( + sret7, + smt.list(vec![ + smt.atom("bvadd"), + sret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + let clsret = declare( + smt, + format!("clsret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + smt.eq(clsret, sret7), + smt.eq( + clsret, + smt.list(vec![ + smt.atom("bvsub"), + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + let cls16ret = declare( + smt, + format!("cls16ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("bvsle"), + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + x, + ]), + smt.eq(cls16ret, clzret), + smt.eq(cls16ret, clsret), + ])); + + cls16ret +} + +pub fn cls8(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(7, 0, x); + + // Generated code. + // total zeros counter + let zret0 = declare( + smt, + format!("zret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq( + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + )); + // round 1 + let zret4 = declare( + smt, + format!("zret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let zy4 = declare( + smt, + format!("zy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let zx4 = declare( + smt, + format!("zx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(zy4, smt.bvlshr(x, smt.atom("#x04")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(zret4, zret0), + smt.eq( + zret4, + smt.list(vec![ + smt.atom("bvadd"), + zret0, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(zx4, zy4), + smt.eq(zx4, x), + ])); + // round 2 + let zret5 = declare( + smt, + format!("zret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let zy2 = declare( + smt, + format!("zy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let zx2 = declare( + smt, + format!("zx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(zy2, smt.bvlshr(zx4, smt.atom("#x02")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(zret5, zret4), + smt.eq( + zret5, + smt.list(vec![ + smt.atom("bvadd"), + zret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(zx2, zy2), + smt.eq(zx2, zx4), + ])); + // round 3 + let zret6 = declare( + smt, + format!("zret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let zy1 = declare( + smt, + format!("zy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let zx1 = declare( + smt, + format!("zx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(zy1, smt.bvlshr(zx2, smt.atom("#x01")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(zret6, zret5), + smt.eq( + zret6, + smt.list(vec![ + smt.atom("bvadd"), + zret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zy1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(zx1, zy1), + smt.eq(zx1, zx2), + ])); + // last round + let zret7 = declare( + smt, + format!("zret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + zx1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(zret7, zret6), + smt.eq( + zret7, + smt.list(vec![ + smt.atom("bvadd"), + zret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + let clzret = declare( + smt, + format!("clzret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + smt.eq(clzret, zret7), + smt.eq( + clzret, + smt.list(vec![ + smt.atom("bvsub"), + zret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + // total zeros counter + let sret0 = declare( + smt, + format!("sret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq( + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + )); + // round 1 + let sret4 = declare( + smt, + format!("sret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let sy4 = declare( + smt, + format!("sy4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let sx4 = declare( + smt, + format!("sx4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(sy4, smt.bvashr(x, smt.atom("#x04")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![smt.atoms().und, smt.atom("bv255"), smt.numeral(8)]), + ), + ]), + smt.eq(sret4, sret0), + smt.eq( + sret4, + smt.list(vec![ + smt.atom("bvadd"), + sret0, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy4, + smt.list(vec![smt.atoms().und, smt.atom("bv255"), smt.numeral(8)]), + ), + ]), + smt.eq(sx4, sy4), + smt.eq(sx4, x), + ])); + // round 2 + let sret5 = declare( + smt, + format!("sret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let sy2 = declare( + smt, + format!("sy2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let sx2 = declare( + smt, + format!("sx2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(sy2, smt.bvashr(sx4, smt.atom("#x02")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![smt.atoms().und, smt.atom("bv255"), smt.numeral(8)]), + ), + ]), + smt.eq(sret5, sret4), + smt.eq( + sret5, + smt.list(vec![ + smt.atom("bvadd"), + sret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy2, + smt.list(vec![smt.atoms().und, smt.atom("bv255"), smt.numeral(8)]), + ), + ]), + smt.eq(sx2, sy2), + smt.eq(sx2, sx4), + ])); + // round 3 + let sret6 = declare( + smt, + format!("sret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let sy1 = declare( + smt, + format!("sy1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let sx1 = declare( + smt, + format!("sx1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(sy1, smt.bvashr(sx2, smt.atom("#x01")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![smt.atoms().und, smt.atom("bv255"), smt.numeral(8)]), + ), + ]), + smt.eq(sret6, sret5), + smt.eq( + sret6, + smt.list(vec![ + smt.atom("bvadd"), + sret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sy1, + smt.list(vec![smt.atoms().und, smt.atom("bv255"), smt.numeral(8)]), + ), + ]), + smt.eq(sx1, sy1), + smt.eq(sx1, sx2), + ])); + // last round + let sret7 = declare( + smt, + format!("sret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + sx1, + smt.list(vec![smt.atoms().und, smt.atom("bv255"), smt.numeral(8)]), + ), + ]), + smt.eq(sret7, sret6), + smt.eq( + sret7, + smt.list(vec![ + smt.atom("bvadd"), + sret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + let clsret = declare( + smt, + format!("clsret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.eq( + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + smt.eq(clsret, sret7), + smt.eq( + clsret, + smt.list(vec![ + smt.atom("bvsub"), + sret7, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + let cls8ret = declare( + smt, + format!("cls8ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("bvsle"), + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + x, + ]), + smt.eq(cls8ret, clzret), + smt.eq(cls8ret, clsret), + ])); + + cls8ret +} + +pub fn cls1(smt: &mut Context, id: usize) -> SExpr { + // Generated code. + let cls1ret = declare( + smt, + format!("cls1ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(1)]), + ); + let _ = smt.assert(smt.eq( + cls1ret, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(1)]), + )); + + cls1ret +} diff --git a/cranelift/isle/veri/veri/src/encoded/clz.rs b/cranelift/isle/veri/veri/src/encoded/clz.rs new file mode 100644 index 000000000000..d54588cf4920 --- /dev/null +++ b/cranelift/isle/veri/veri/src/encoded/clz.rs @@ -0,0 +1,1056 @@ +// Adapted from https://stackoverflow.com/questions/23856596/how-to-count-leading-zeros-in-a-32-bit-unsigned-integer +use easy_smt::*; + +fn declare(smt: &mut Context, name: String, val: SExpr) -> SExpr { + smt.declare_const(name.clone(), val).unwrap(); + smt.atom(name) +} + +pub fn clz64(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + // Generated code. + // total zeros counter + let ret0 = declare( + smt, + format!("ret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + ret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + )); + // round 1 + let ret1 = declare( + smt, + format!("ret1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let y32 = declare( + smt, + format!("y32_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let x32 = declare( + smt, + format!("x32_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(y32, smt.bvlshr(x, smt.atom("#x0000000000000020")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y32, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(ret1, ret0), + smt.eq( + ret1, + smt.list(vec![ + smt.atom("bvadd"), + ret0, + smt.list(vec![smt.atoms().und, smt.atom("bv32"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y32, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(x32, y32), + smt.eq(x32, x), + ])); + // round 2 + let ret2 = declare( + smt, + format!("ret2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let y16 = declare( + smt, + format!("y16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let x16 = declare( + smt, + format!("x16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(y16, smt.bvlshr(x32, smt.atom("#x0000000000000010")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(ret2, ret1), + smt.eq( + ret2, + smt.list(vec![ + smt.atom("bvadd"), + ret1, + smt.list(vec![smt.atoms().und, smt.atom("bv16"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(x16, y16), + smt.eq(x16, x32), + ])); + // round 3 + let ret3 = declare( + smt, + format!("ret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let y8 = declare( + smt, + format!("y8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let x8 = declare( + smt, + format!("x8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(y8, smt.bvlshr(x16, smt.atom("#x0000000000000008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(ret3, ret2), + smt.eq( + ret3, + smt.list(vec![ + smt.atom("bvadd"), + ret2, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(x8, y8), + smt.eq(x8, x16), + ])); + // round 4 + let ret4 = declare( + smt, + format!("ret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let y4 = declare( + smt, + format!("y4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let x4 = declare( + smt, + format!("x4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(y4, smt.bvlshr(x8, smt.atom("#x0000000000000004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(ret4, ret3), + smt.eq( + ret4, + smt.list(vec![ + smt.atom("bvadd"), + ret3, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(x4, y4), + smt.eq(x4, x8), + ])); + // round 5 + let ret5 = declare( + smt, + format!("ret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let y2 = declare( + smt, + format!("y2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(y2, smt.bvlshr(x4, smt.atom("#x0000000000000002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(ret5, ret4), + smt.eq( + ret5, + smt.list(vec![ + smt.atom("bvadd"), + ret4, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(x2, y2), + smt.eq(x2, x4), + ])); + // round 6 + let ret6 = declare( + smt, + format!("ret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let y1 = declare( + smt, + format!("y1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq(y1, smt.bvlshr(x2, smt.atom("#x0000000000000001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(ret6, ret5), + smt.eq( + ret6, + smt.list(vec![ + smt.atom("bvadd"), + ret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(x1, y1), + smt.eq(x1, x2), + ])); + + // last round + let ret7 = declare( + smt, + format!("ret7_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + x1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(64)]), + ), + ]), + smt.eq(ret7, ret6), + smt.eq( + ret7, + smt.list(vec![ + smt.atom("bvadd"), + ret6, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(64)]), + ]), + ), + ])); + + ret7 +} + +pub fn clz32(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(31, 0, x); + + // Generated code. + // total zeros counter + let ret0 = declare( + smt, + format!("ret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + ret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + )); + // round 1 + let ret1 = declare( + smt, + format!("ret1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let y16 = declare( + smt, + format!("y16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let x16 = declare( + smt, + format!("x16_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(y16, smt.bvlshr(x, smt.atom("#x00000010")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(ret1, ret0), + smt.eq( + ret1, + smt.list(vec![ + smt.atom("bvadd"), + ret0, + smt.list(vec![smt.atoms().und, smt.atom("bv16"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y16, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(x16, y16), + smt.eq(x16, x), + ])); + // round 2 + let ret2 = declare( + smt, + format!("ret2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let y8 = declare( + smt, + format!("y8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let x8 = declare( + smt, + format!("x8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(y8, smt.bvlshr(x16, smt.atom("#x00000008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(ret2, ret1), + smt.eq( + ret2, + smt.list(vec![ + smt.atom("bvadd"), + ret1, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(x8, y8), + smt.eq(x8, x16), + ])); + // round 3 + let ret3 = declare( + smt, + format!("ret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let y4 = declare( + smt, + format!("y4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let x4 = declare( + smt, + format!("x4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(y4, smt.bvlshr(x8, smt.atom("#x00000004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(ret3, ret2), + smt.eq( + ret3, + smt.list(vec![ + smt.atom("bvadd"), + ret2, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(x4, y4), + smt.eq(x4, x8), + ])); + // round 4 + let ret4 = declare( + smt, + format!("ret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let y2 = declare( + smt, + format!("y2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(y2, smt.bvlshr(x4, smt.atom("#x00000002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(ret4, ret3), + smt.eq( + ret4, + smt.list(vec![ + smt.atom("bvadd"), + ret3, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(x2, y2), + smt.eq(x2, x4), + ])); + // round 5 + let ret5 = declare( + smt, + format!("ret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let y1 = declare( + smt, + format!("y1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq(y1, smt.bvlshr(x2, smt.atom("#x00000001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(ret5, ret4), + smt.eq( + ret5, + smt.list(vec![ + smt.atom("bvadd"), + ret4, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(x1, y1), + smt.eq(x1, x2), + ])); + + // last round + let ret6 = declare( + smt, + format!("ret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + x1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(32)]), + ), + ]), + smt.eq(ret6, ret5), + smt.eq( + ret6, + smt.list(vec![ + smt.atom("bvadd"), + ret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(32)]), + ]), + ), + ])); + ret6 +} + +pub fn clz16(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(15, 0, x); + + // Generated code. + // total zeros counter + let ret1 = declare( + smt, + format!("ret1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq( + ret1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + )); + // round 1 + let ret2 = declare( + smt, + format!("ret2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let y8 = declare( + smt, + format!("y8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let x8 = declare( + smt, + format!("x8_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(y8, smt.bvlshr(x, smt.atom("#x0008")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(ret2, ret1), + smt.eq( + ret2, + smt.list(vec![ + smt.atom("bvadd"), + ret1, + smt.list(vec![smt.atoms().und, smt.atom("bv8"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y8, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(x8, y8), + smt.eq(x8, x), + ])); + // round 2 + let ret3 = declare( + smt, + format!("ret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let y4 = declare( + smt, + format!("y4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let x4 = declare( + smt, + format!("x4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(y4, smt.bvlshr(x8, smt.atom("#x0004")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(ret3, ret2), + smt.eq( + ret3, + smt.list(vec![ + smt.atom("bvadd"), + ret2, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(x4, y4), + smt.eq(x4, x8), + ])); + // round 3 + let ret4 = declare( + smt, + format!("ret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let y2 = declare( + smt, + format!("y2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(y2, smt.bvlshr(x4, smt.atom("#x0002")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(ret4, ret3), + smt.eq( + ret4, + smt.list(vec![ + smt.atom("bvadd"), + ret3, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(x2, y2), + smt.eq(x2, x4), + ])); + // round 4 + let ret5 = declare( + smt, + format!("ret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let y1 = declare( + smt, + format!("y1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq(y1, smt.bvlshr(x2, smt.atom("#x0001")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(ret5, ret4), + smt.eq( + ret5, + smt.list(vec![ + smt.atom("bvadd"), + ret4, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(x1, y1), + smt.eq(x1, x2), + ])); + + // last round + let ret6 = declare( + smt, + format!("ret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + x1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(16)]), + ), + ]), + smt.eq(ret6, ret5), + smt.eq( + ret6, + smt.list(vec![ + smt.atom("bvadd"), + ret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(16)]), + ]), + ), + ])); + ret6 +} + +pub fn clz8(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(7, 0, x); + + // Generated code. + // total zeros counter + let ret0 = declare( + smt, + format!("ret0_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq( + ret0, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + )); + // round 1 + let ret3 = declare( + smt, + format!("ret3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let y4 = declare( + smt, + format!("y4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let x4 = declare( + smt, + format!("x4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(y4, smt.bvlshr(x, smt.atom("#x04")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(ret3, ret0), + smt.eq( + ret3, + smt.list(vec![ + smt.atom("bvadd"), + ret0, + smt.list(vec![smt.atoms().und, smt.atom("bv4"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y4, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(x4, y4), + smt.eq(x4, x), + ])); + // round 2 + let ret4 = declare( + smt, + format!("ret4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let y2 = declare( + smt, + format!("y2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(y2, smt.bvlshr(x4, smt.atom("#x02")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(ret4, ret3), + smt.eq( + ret4, + smt.list(vec![ + smt.atom("bvadd"), + ret3, + smt.list(vec![smt.atoms().und, smt.atom("bv2"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y2, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(x2, y2), + smt.eq(x2, x4), + ])); + // round 3 + let ret5 = declare( + smt, + format!("ret5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let y1 = declare( + smt, + format!("y1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq(y1, smt.bvlshr(x2, smt.atom("#x01")))); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(ret5, ret4), + smt.eq( + ret5, + smt.list(vec![ + smt.atom("bvadd"), + ret4, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + y1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(x1, y1), + smt.eq(x1, x2), + ])); + // last round + let ret6 = declare( + smt, + format!("ret6_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.list(vec![ + smt.atom("ite"), + smt.list(vec![ + smt.atom("not"), + smt.eq( + x1, + smt.list(vec![smt.atoms().und, smt.atom("bv0"), smt.numeral(8)]), + ), + ]), + smt.eq(ret6, ret5), + smt.eq( + ret6, + smt.list(vec![ + smt.atom("bvadd"), + ret5, + smt.list(vec![smt.atoms().und, smt.atom("bv1"), smt.numeral(8)]), + ]), + ), + ])); + + ret6 +} + +pub fn clz1(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(0, 0, x); + + // Generated code. + let clz1ret = declare( + smt, + format!("clz1ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(1)]), + ); + let _ = smt.assert(smt.eq(clz1ret, smt.list(vec![smt.atom("bvnot"), x]))); + + clz1ret +} diff --git a/cranelift/isle/veri/veri/src/encoded/mod.rs b/cranelift/isle/veri/veri/src/encoded/mod.rs new file mode 100644 index 000000000000..3f837fccd35d --- /dev/null +++ b/cranelift/isle/veri/veri/src/encoded/mod.rs @@ -0,0 +1,4 @@ +pub mod cls; +pub mod clz; +pub mod popcnt; +pub mod rev; diff --git a/cranelift/isle/veri/veri/src/encoded/popcnt.rs b/cranelift/isle/veri/veri/src/encoded/popcnt.rs new file mode 100644 index 000000000000..47f27c3cd55b --- /dev/null +++ b/cranelift/isle/veri/veri/src/encoded/popcnt.rs @@ -0,0 +1,46 @@ +use easy_smt::*; + +fn declare(smt: &mut Context, name: String, val: SExpr) -> SExpr { + smt.declare_const(name.clone(), val).unwrap(); + smt.atom(name) +} + +fn zero_extend(smt: &mut Context, padding: usize, value: SExpr) -> SExpr { + if padding == 0 { + return value; + } + smt.list(vec![ + smt.list(vec![ + smt.atoms().und, + smt.atom("zero_extend"), + smt.numeral(padding), + ]), + value, + ]) +} + +pub fn popcnt(smt: &mut Context, ty: usize, x: SExpr, id: usize) -> SExpr { + log::debug!("popcnt encoding: {ty}"); + // Only use the number of bits necessary to calculate the result + // max = 2^(n-1) - 1; n = floor(log_2(n)) + 1 + let bits_for_result: usize = ty.ilog2().try_into().unwrap(); + let bits_for_result = bits_for_result + 1; + let mut bits: Vec<_> = (0..ty) + .map(|i| zero_extend(smt, bits_for_result - 1, smt.extract(i as i32, i as i32, x))) + .collect(); + let initial = bits.pop().unwrap(); + let r = bits.iter().fold(initial, |a, b| smt.bvadd(a, *b)); + + let id = format!("{ty}_{id}"); + let result = declare( + smt, + format!("popcnt_{id}"), + smt.list(vec![ + smt.atoms().und, + smt.atom("BitVec"), + smt.numeral(bits_for_result), + ]), + ); + smt.assert(smt.eq(result, r)).unwrap(); + zero_extend(smt, ty - bits_for_result, result) +} diff --git a/cranelift/isle/veri/veri/src/encoded/rev.rs b/cranelift/isle/veri/veri/src/encoded/rev.rs new file mode 100644 index 000000000000..98b30952fdd6 --- /dev/null +++ b/cranelift/isle/veri/veri/src/encoded/rev.rs @@ -0,0 +1,326 @@ +use easy_smt::*; + +fn declare(smt: &mut Context, name: String, val: SExpr) -> SExpr { + smt.declare_const(name.clone(), val).unwrap(); + smt.atom(name) +} + +pub fn rev64(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + // Generated code. + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + x1, + smt.bvor( + smt.bvlshr(x, smt.atom("#x0000000000000020")), + smt.bvshl(x, smt.atom("#x0000000000000020")), + ), + )); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + x2, + smt.bvor( + smt.bvlshr( + smt.bvand(x1, smt.atom("#xffff0000ffff0000")), + smt.atom("#x0000000000000010"), + ), + smt.bvshl( + smt.bvand(x1, smt.atom("#x0000ffff0000ffff")), + smt.atom("#x0000000000000010"), + ), + ), + )); + let x3 = declare( + smt, + format!("x3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + x3, + smt.bvor( + smt.bvlshr( + smt.bvand(x2, smt.atom("#xff00ff00ff00ff00")), + smt.atom("#x0000000000000008"), + ), + smt.bvshl( + smt.bvand(x2, smt.atom("#x00ff00ff00ff00ff")), + smt.atom("#x0000000000000008"), + ), + ), + )); + let x4 = declare( + smt, + format!("x4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + x4, + smt.bvor( + smt.bvlshr( + smt.bvand(x3, smt.atom("#xf0f0f0f0f0f0f0f0")), + smt.atom("#x0000000000000004"), + ), + smt.bvshl( + smt.bvand(x3, smt.atom("#x0f0f0f0f0f0f0f0f")), + smt.atom("#x0000000000000004"), + ), + ), + )); + let x5 = declare( + smt, + format!("x5_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + x5, + smt.bvor( + smt.bvlshr( + smt.bvand(x4, smt.atom("#xcccccccccccccccc")), + smt.atom("#x0000000000000002"), + ), + smt.bvshl( + smt.bvand(x4, smt.atom("#x3333333333333333")), + smt.atom("#x0000000000000002"), + ), + ), + )); + let rev64ret = declare( + smt, + format!("rev64ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(64)]), + ); + let _ = smt.assert(smt.eq( + rev64ret, + smt.bvor( + smt.bvlshr( + smt.bvand(x5, smt.atom("#xaaaaaaaaaaaaaaaa")), + smt.atom("#x0000000000000001"), + ), + smt.bvshl( + smt.bvand(x5, smt.atom("#x5555555555555555")), + smt.atom("#x0000000000000001"), + ), + ), + )); + + rev64ret +} + +pub fn rev32(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(31, 0, x); + + // Generated code. + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + x1, + smt.bvor( + smt.bvlshr(x, smt.atom("#x00000010")), + smt.bvshl(x, smt.atom("#x00000010")), + ), + )); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + x2, + smt.bvor( + smt.bvlshr( + smt.bvand(x1, smt.atom("#xff00ff00")), + smt.atom("#x00000008"), + ), + smt.bvshl( + smt.bvand(x1, smt.atom("#x00ff00ff")), + smt.atom("#x00000008"), + ), + ), + )); + let x3 = declare( + smt, + format!("x3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + x3, + smt.bvor( + smt.bvlshr( + smt.bvand(x2, smt.atom("#xf0f0f0f0")), + smt.atom("#x00000004"), + ), + smt.bvshl( + smt.bvand(x2, smt.atom("#x0f0f0f0f")), + smt.atom("#x00000004"), + ), + ), + )); + let x4 = declare( + smt, + format!("x4_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + x4, + smt.bvor( + smt.bvlshr( + smt.bvand(x3, smt.atom("#xcccccccc")), + smt.atom("#x00000002"), + ), + smt.bvshl( + smt.bvand(x3, smt.atom("#x33333333")), + smt.atom("#x00000002"), + ), + ), + )); + let rev32ret = declare( + smt, + format!("rev32ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(32)]), + ); + let _ = smt.assert(smt.eq( + rev32ret, + smt.bvor( + smt.bvlshr( + smt.bvand(x4, smt.atom("#xaaaaaaaa")), + smt.atom("#x00000001"), + ), + smt.bvshl( + smt.bvand(x4, smt.atom("#x55555555")), + smt.atom("#x00000001"), + ), + ), + )); + + rev32ret +} + +pub fn rev16(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(15, 0, x); + + // Generated code. + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq( + x1, + smt.bvor( + smt.bvlshr(x, smt.atom("#x0008")), + smt.bvshl(x, smt.atom("#x0008")), + ), + )); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq( + x2, + smt.bvor( + smt.bvlshr(smt.bvand(x1, smt.atom("#xf0f0")), smt.atom("#x0004")), + smt.bvshl(smt.bvand(x1, smt.atom("#x0f0f")), smt.atom("#x0004")), + ), + )); + let x3 = declare( + smt, + format!("x3_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq( + x3, + smt.bvor( + smt.bvlshr(smt.bvand(x2, smt.atom("#xcccc")), smt.atom("#x0002")), + smt.bvshl(smt.bvand(x2, smt.atom("#x3333")), smt.atom("#x0002")), + ), + )); + let rev16ret = declare( + smt, + format!("rev16ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(16)]), + ); + let _ = smt.assert(smt.eq( + rev16ret, + smt.bvor( + smt.bvlshr(smt.bvand(x3, smt.atom("#xaaaa")), smt.atom("#x0001")), + smt.bvshl(smt.bvand(x3, smt.atom("#x5555")), smt.atom("#x0001")), + ), + )); + + // let padding = smt.new_fresh_bits(smt.bitwidth - 16); + // smt.concat(padding, rev16ret) + rev16ret +} + +pub fn rev8(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(7, 0, x); + + // Generated code. + let x1 = declare( + smt, + format!("x1_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq( + x1, + smt.bvor( + smt.bvlshr(x, smt.atom("#x04")), + smt.bvshl(x, smt.atom("#x04")), + ), + )); + let x2 = declare( + smt, + format!("x2_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq( + x2, + smt.bvor( + smt.bvlshr(smt.bvand(x1, smt.atom("#xcc")), smt.atom("#x02")), + smt.bvshl(smt.bvand(x1, smt.atom("#x33")), smt.atom("#x02")), + ), + )); + let rev8ret = declare( + smt, + format!("rev8ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(8)]), + ); + let _ = smt.assert(smt.eq( + rev8ret, + smt.bvor( + smt.bvlshr(smt.bvand(x2, smt.atom("#xaa")), smt.atom("#x01")), + smt.bvshl(smt.bvand(x2, smt.atom("#x55")), smt.atom("#x01")), + ), + )); + + // let padding = smt.new_fresh_bits(smt.bitwidth - 8); + // smt.concat(padding, rev8ret) + rev8ret +} + +pub fn rev1(smt: &mut Context, x: SExpr, id: usize) -> SExpr { + let x = smt.extract(0, 0, x); + + // Generated code. + let rev1ret = declare( + smt, + format!("rev1ret_{id}", id = id), + smt.list(vec![smt.atoms().und, smt.atom("BitVec"), smt.numeral(1)]), + ); + let _ = smt.assert(smt.eq(rev1ret, x)); + + // let padding = smt.new_fresh_bits(smt.bitwidth - 1); + // smt.concat(padding, rev1ret) + rev1ret +} diff --git a/cranelift/isle/veri/veri/src/expand.rs b/cranelift/isle/veri/veri/src/expand.rs new file mode 100644 index 000000000000..a96e268bbfa9 --- /dev/null +++ b/cranelift/isle/veri/veri/src/expand.rs @@ -0,0 +1,890 @@ +use crate::{program::Program, reachability::Reachability}; +use anyhow::{Result, bail, format_err}; +use cranelift_isle::{ + disjointsets::DisjointSets, + sema::{RuleId, TermId}, + trie_again::{Binding, BindingId, Constraint, Rule, RuleSet}, +}; +use std::collections::{HashMap, HashSet}; + +#[derive(Debug, Clone)] +pub enum Constrain { + Match(BindingId, Constraint), + NotAll(Vec), +} + +impl Constrain { + fn bindings(&self) -> Vec { + match self { + Constrain::Match(binding_id, _) => vec![*binding_id], + Constrain::NotAll(constrains) => constrains.iter().flat_map(|c| c.bindings()).collect(), + } + } + + fn substitute(&self, reindex: &Reindex) -> Self { + match self { + Constrain::Match(binding_id, constraint) => { + Constrain::Match(reindex.id(*binding_id), *constraint) + } + Constrain::NotAll(constrains) => { + Constrain::NotAll(constrains.iter().map(|c| c.substitute(reindex)).collect()) + } + } + } +} + +#[derive(Debug, Clone)] +pub struct Expansion { + pub term: TermId, + pub rules: Vec, + pub negated: Vec, + pub bindings: Vec>, + pub constraints: Vec, + pub equals: DisjointSets, + pub parameters: Vec, + pub result: BindingId, +} + +impl Expansion { + /// Check basic feasibility of the expansion. + /// + /// A false return value means the expansion is known to never be viable. + /// However, a successful feasibility check leaves open the possibility that + /// the expansion is inapplicable for other reasons. + pub fn is_feasible(&self) -> bool { + // Assert data structure invariants. + self.validate(); + + // Check if any constraints are incompatible. + for constrain in &self.constraints { + match constrain { + Constrain::Match(binding_id, constraint) => { + let binding = self.bindings[binding_id.index()] + .as_ref() + .expect("constrained binding must be defined"); + if !constraint.compatible(binding) { + return false; + } + } + Constrain::NotAll(_) => { + // Conservatively assume negated constraints can be met. + continue; + } + } + } + + true + } + + fn constrain(&mut self, constrain: Constrain) { + self.constraints.push(constrain); + } + + fn add_match(&mut self, binding_id: BindingId, constraint: Constraint) { + self.constrain(Constrain::Match(binding_id, constraint)); + } + + fn push_binding(&mut self, binding: Binding) -> BindingId { + let binding_id = self.bindings.len().try_into().unwrap(); + self.bindings.push(Some(binding)); + binding_id + } + + fn validate(&self) { + // TODO(mbm): return errors in expansion validation rather than assert? + + // Bindings: all references should be valid. + for binding in self.bindings.iter().flatten() { + for source in binding.sources() { + assert!(self.is_defined(*source)); + } + } + + // Constraints: should refer to defined bindings. + for constrain in &self.constraints { + for binding_id in constrain.bindings() { + assert!(self.is_defined(binding_id)); + } + } + + // Parameters: should be defined argument bindings. + for binding in &self.parameters { + assert!(matches!( + self.binding(*binding), + Some(Binding::Argument { .. }) + )); + } + + // Result: should be defined. + assert!(self.is_defined(self.result)); + } + + fn is_defined(&self, binding_id: BindingId) -> bool { + self.binding(binding_id).is_some() + } + + pub fn binding(&self, binding_id: BindingId) -> Option<&Binding> { + self.bindings.get(binding_id.index())?.as_ref() + } + + pub fn equalities(&self) -> Vec<(BindingId, BindingId)> { + let mut eqs = Vec::new(); + for (i, binding) in self.bindings.iter().enumerate() { + if binding.is_none() { + continue; + } + let binding_id = i.try_into().unwrap(); + if let Some(eq) = self.equals.find(binding_id) + && eq != binding_id + { + eqs.push((binding_id, eq)); + } + } + eqs + } + + pub fn terms(&self, prog: &Program) -> Vec { + let mut terms: Vec<_> = self + .bindings + .iter() + .flatten() + .filter_map(|b| b.term(&prog.tyenv, &prog.termenv)) + .collect(); + // TODO(mbm): dedupe and preserve order + terms.sort(); + terms.dedup(); + terms + } + + /// Tags that appear on rules and term in the expansion. + pub fn tags(&self, prog: &Program) -> HashSet { + let mut tags = HashSet::new(); + + // Root term + if let Some(term_tags) = prog.specenv.term_tags.get(&self.term) { + tags = &tags | term_tags; + } + + // Rules + for rule_id in &self.rules { + if let Some(rule_tags) = prog.specenv.rule_tags.get(rule_id) { + tags = &tags | rule_tags; + } + } + + // Terms + for term_id in self.terms(prog) { + if let Some(term_tags) = prog.specenv.term_tags.get(&term_id) { + tags = &tags | term_tags; + } + } + + tags + } + + fn constructor_bindings(&self) -> Vec<(BindingId, TermId)> { + self.bindings + .iter() + .enumerate() + .flat_map(|(i, binding)| match binding { + Some(Binding::Constructor { term, .. }) => Some((i.try_into().unwrap(), *term)), + _ => None, + }) + .collect() + } + + fn substitute(&mut self, target: BindingId, replace: BindingId) { + // Reindex bindings. + let mut reindex = Reindex::new(); + reindex.map(target, replace); + for binding in self.bindings.iter_mut().flatten() { + *binding = reindex.binding(binding); + } + + // Delete the target binding. + self.bindings[target.index()] = None; + + // Constraints. + self.constraints = self + .constraints + .iter() + .map(|c| c.substitute(&reindex)) + .collect(); + + // Result. + self.result = reindex.id(self.result); + } +} + +/// Chaining configuration. +pub struct Chaining<'a> { + prog: &'a Program, + term_rule_sets: &'a HashMap, + reach: Reachability, + exclude: HashSet, + include: HashSet, + include_macros: bool, + max_rules: usize, + default: bool, +} + +impl<'a> Chaining<'a> { + pub fn new(prog: &'a Program, term_rule_sets: &'a HashMap) -> Result { + Ok(Self { + prog, + term_rule_sets, + reach: Reachability::build(term_rule_sets), + include: HashSet::new(), + exclude: HashSet::new(), + include_macros: true, + max_rules: 0, + default: false, + }) + } + + pub fn chain_term(&mut self, term_name: &str) -> Result<()> { + let term_id = self + .prog + .get_term_by_name(term_name) + .ok_or(format_err!("unknown term {term_name}"))?; + self.include.insert(term_id); + Ok(()) + } + + pub fn chain_terms(&mut self, term_names: &Vec) -> Result<()> { + for term_name in term_names { + self.chain_term(term_name)?; + } + Ok(()) + } + + /// Set whether to chain "macro" terms. Macro terms are wrapper terms with + /// only one rule that has no constraints. + pub fn chain_macros(&mut self, enabled: bool) { + self.include_macros = enabled; + } + + pub fn set_max_rules(&mut self, max_rules: usize) { + self.max_rules = max_rules; + } + + pub fn exclude_chain_term(&mut self, term_name: &str) -> Result<()> { + let term_id = self + .prog + .get_term_by_name(term_name) + .ok_or(format_err!("unknown term {term_name}"))?; + self.exclude.insert(term_id); + Ok(()) + } + + pub fn exclude_chain_terms(&mut self, term_names: &Vec) -> Result<()> { + for term_name in term_names { + self.exclude_chain_term(term_name)?; + } + Ok(()) + } + + /// Configure whether terms should be considered for chaining by default, if + /// no other rules apply. + pub fn set_default(&mut self, default: bool) { + self.default = default; + } + + /// Report whether the term has expansions. + /// + /// From the point of view of the expansion graph, this means the term is + /// not a leaf node. Therefore, we can either apply chaining or produce + /// expansions rooted at this term. + pub fn is_expandable(&self, term_id: TermId) -> bool { + // Is it an internal constructor? + let term = self.prog.term(term_id); + if !term.has_internal_constructor() { + return false; + } + + // Term should have rules. + if self.num_rules(term_id) == 0 { + return false; + } + + true + } + + /// Reports whether the given term can be chained. + /// + /// Terms can be chained if they are expandable and acyclic. + pub fn is_chainable(&self, term_id: TermId) -> bool { + // At minimum, it should be a term that has expansions. + if !self.is_expandable(term_id) { + return false; + } + + // Cyclic terms cannot be chained. + if self.reach.is_cyclic(term_id) { + return false; + } + + true + } + + /// Validate chaining configuration. + pub fn validate(&self) -> Result<()> { + for term_id in (0..self.prog.termenv.terms.len()).map(TermId) { + if self.has_chain_attribute(term_id) && !self.is_chainable(term_id) { + bail!( + "term '{name}' has chain attribute but is not chainable", + name = self.prog.term_name(term_id) + ); + } + } + Ok(()) + } + + pub fn should_chain(&self, term_id: TermId) -> bool { + // Check baseline requirements. + if !self.is_chainable(term_id) { + return false; + } + + // Terms with specs should not be chained. + if self.prog.specenv.has_spec(term_id) { + return false; + } + + // Explicit exclusions. + if self.exclude.contains(&term_id) { + return false; + } + + // Explicit inclusions. + if self.include.contains(&term_id) { + return true; + } + + // Marked with chaining attribute. + if self.prog.specenv.chain.contains(&term_id) { + return true; + } + + // Chain macros, if configured. + if self.include_macros && self.is_macro(term_id) { + return true; + } + + // Max rules threshold, if set. + if self.max_rules > 0 && self.num_rules(term_id) > self.max_rules { + return false; + } + + // Default fallback. + self.default + } + + fn has_chain_attribute(&self, term_id: TermId) -> bool { + self.prog.specenv.chain.contains(&term_id) + } + + fn num_rules(&self, term_id: TermId) -> usize { + self.term_rule_sets + .get(&term_id) + .map(|rule_set| rule_set.rules.len()) + .unwrap_or_default() + } + + fn is_macro(&self, term_id: TermId) -> bool { + // "Macro" terms have only one rule. + if self.num_rules(term_id) != 1 { + return false; + } + + // Rule should be "trivial". + let rule = &self.term_rule_sets[&term_id].rules[0]; + rule.total_constraints() == 0 + } +} + +/// Partially completed expansion. +struct Partial { + /// Current state of the expansion. + expansion: Expansion, + + /// Stack of bindings to apply chaining to. May be non-exhaustive. + chain_candidates: Vec, +} + +pub struct Expander<'a> { + prog: &'a Program, + term_rule_sets: &'a HashMap, + + /// Chaining configuration: which terms should be chained. + chaining: Chaining<'a>, + + /// Whether to drop expansions as soon as they are deemed to be infeasible. + prune_infeasible: bool, + + /// Expansions under construction. + stack: Vec, + + /// Root terms expansion has been initiated for. + roots: HashSet, + + /// Completed expansions. + complete: Vec, +} + +impl<'a> Expander<'a> { + pub fn new( + prog: &'a Program, + term_rule_sets: &'a HashMap, + chaining: Chaining<'a>, + ) -> Self { + Self { + prog, + term_rule_sets, + chaining, + prune_infeasible: true, + stack: Vec::new(), + roots: HashSet::new(), + complete: Vec::new(), + } + } + + /// Add the given named term as an expansion root. + pub fn add_root_term_name(&mut self, term_name: &str) -> Result<()> { + let term_id = self + .prog + .get_term_by_name(term_name) + .ok_or(format_err!("unknown term {term_name}"))?; + self.add_root(term_id); + Ok(()) + } + + /// Add the given term as an expansion root. That is, start expanding rules + /// from this point. + pub fn add_root(&mut self, term_id: TermId) { + // Skip if the root has already been added. + if self.roots.contains(&term_id) { + return; + } + + // Initialize an expansion at this root. + self.constructor(term_id); + + // Record root. + self.roots.insert(term_id); + } + + // Push an initial expansion for a constructor call of the given term. + fn constructor(&mut self, term_id: TermId) { + // Lookup term. + let term = self.prog.term(term_id); + assert!(term.has_constructor()); + + // Push argument bindings. + let sig = term + .constructor_sig(&self.prog.tyenv) + .expect("should have constructor signature"); + let mut bindings = Vec::new(); + let mut parameters = Vec::new(); + for i in 0..sig.param_tys.len() { + let parameter = bindings.len().try_into().unwrap(); + bindings.push(Some(Binding::Argument { + index: i.try_into().unwrap(), + })); + parameters.push(parameter); + } + + // Binding for the constructor call. + let result = bindings.len().try_into().unwrap(); + bindings.push(Some(Binding::Constructor { + term: term_id, + parameters: parameters.clone().into(), + instance: 0, + })); + + // Root constructor call should be the first term to be expanded. + let chain_candidates = vec![result]; + + // Store. + let expansion = Expansion { + term: term_id, + rules: Vec::new(), + negated: Vec::new(), + bindings, + constraints: Vec::new(), + equals: DisjointSets::default(), + parameters, + result, + }; + assert!(expansion.is_feasible()); + self.stack.push(Partial { + expansion, + chain_candidates, + }); + } + + /// Set whether to prune infeasible expansions. If enabled, expansions will + /// be dropped as soon as they are deemed to be not feasible. + pub fn set_prune_infeasible(&mut self, enabled: bool) { + self.prune_infeasible = enabled; + } + + fn finish(&mut self, expansion: Expansion) { + // Cascade into any remaining constructors. + // + // Any remaining constructor calls could not be chained. Therefore, in + // order to consider rules that apply to these terms, we need to + // initiate expansion from them as root terms. We only do so for terms + // that have an explicit specification: spec-less terms are assumed to + // be part of some real chain and are not verified standalone. + for (_, term_id) in expansion.constructor_bindings() { + if self.chaining.is_expandable(term_id) && self.prog.specenv.has_spec(term_id) { + self.add_root(term_id); + } + } + + // Add to completed list. + // + // As an internal consistency check, ensure we have produced a valid + // result. + expansion.validate(); + self.complete.push(expansion); + } + + pub fn chaining(&self) -> &Chaining<'_> { + &self.chaining + } + + pub fn expansions(&self) -> &Vec { + &self.complete + } + + pub fn expand(&mut self) { + while let Some(partial) = self.stack.pop() { + self.extend(partial); + } + } + + fn extend(&mut self, partial: Partial) { + let Partial { + expansion, + chain_candidates, + } = partial; + + // Determine candidates for chaining. + // + // If we have pre-existing candidates at this stage, we'll process + // those. Otherwise, revisit the expansion and see if previous chain + // applications have produced more candidates. + let mut chain_candidates = if !chain_candidates.is_empty() { + chain_candidates + } else { + self.chain_candidates(&expansion) + }; + + // Select a candidate to chain. If none, we're done. + let Some(chain_binding_id) = chain_candidates.pop() else { + self.finish(expansion); + return; + }; + + // Chain constructor. + let binding = &expansion.bindings[chain_binding_id.index()]; + let Some(Binding::Constructor { + term, parameters, .. + }) = binding + else { + unreachable!("expect constructor binding") + }; + + let rule_set = &self.term_rule_sets[term]; + assert!(!rule_set.rules.is_empty()); + for rule in rule_set.rules.iter().rev() { + // Apply rule. + let mut apply = Application::new(expansion.clone()); + apply.rule(rule_set, rule, parameters, chain_binding_id); + + // Apply negations of higher-priority overlapping rules that are + // tagged to indicate priority is significant. + if let Some(overlaps) = self.prog.overlaps.get(&rule.id) { + for other in rule_set.rules.iter().rev() { + let priority_significant = self.prog.specenv.priority.contains(&other.id); + if priority_significant && overlaps.contains(&other.id) { + apply.negation(rule_set, other); + } + } + } + + // Finalize the chaining application. + let chained = apply.build(); + + // Push onto stack for further expansion, optionally checking if the + // current partial state is feasible. + if !self.prune_infeasible || chained.is_feasible() { + self.stack.push(Partial { + expansion: chained, + chain_candidates: chain_candidates.clone(), + }); + } + } + } + + // Identify bindings that could be chained. + fn chain_candidates(&mut self, expansion: &Expansion) -> Vec { + expansion + .constructor_bindings() + .iter() + .filter_map(|(binding_id, term_id)| { + if self.chaining.should_chain(*term_id) { + Some(*binding_id) + } else { + None + } + }) + .collect() + } +} + +struct Substitution { + target: BindingId, + replace: BindingId, +} + +struct Application { + expansion: Expansion, + substitutions: Vec, + import_reindex: Reindex, +} + +impl Application { + fn new(expansion: Expansion) -> Self { + Self { + expansion, + substitutions: Vec::new(), + import_reindex: Reindex::new(), + } + } + + fn rule( + &mut self, + rule_set: &RuleSet, + rule: &Rule, + parameters: &[BindingId], + call_site: BindingId, + ) { + // Record the application of this rule. + self.expansion.rules.push(rule.id); + + // Arguments. + for (i, parameter) in parameters.iter().enumerate() { + // Lookup binding ID from the source rule. + let arg = Binding::Argument { + index: i.try_into().unwrap(), + }; + let binding_id = rule_set + .find_binding(&arg) + .expect("should have argument binding"); + + // Import into expansion. + let arg_binding_id = self.add_binding(rule_set, binding_id); + + // Substitute argument with the parameter. + self.substitutions.push(Substitution { + target: arg_binding_id, + replace: *parameter, + }); + } + + // Constraints. + for i in 0..rule_set.bindings.len() { + let binding_id = i.try_into().unwrap(); + if let Some(constraint) = rule.get_constraint(binding_id) { + let expansion_binding_id = self.add_binding(rule_set, binding_id); + self.expansion.add_match(expansion_binding_id, constraint); + } + } + + // Equals. + for i in 0..rule_set.bindings.len() { + let binding_id = i.try_into().unwrap(); + if let Some(equal_binding_id) = rule.equals.find(binding_id) + && equal_binding_id != binding_id + { + let expansion_binding_id = self.add_binding(rule_set, binding_id); + let expansion_equal_binding_id = self.add_binding(rule_set, equal_binding_id); + self.expansion + .equals + .merge(expansion_binding_id, expansion_equal_binding_id); + } + } + + // TODO: iterators, prio? + + // Impure. + for impure_binding_id in &rule.impure { + self.add_binding(rule_set, *impure_binding_id); + } + + // Result. + // + // Once imported, the callsite should be substituted for the result binding. + let result_binding_id = self.add_binding(rule_set, rule.result); + self.substitutions.push(Substitution { + target: call_site, + replace: result_binding_id, + }); + } + + fn build(mut self) -> Expansion { + // Process substitutions. + for substitution in self.substitutions.iter().rev() { + self.expansion + .substitute(substitution.target, substitution.replace); + } + + // Return expanded rule. + self.expansion + } + + fn negation(&mut self, rule_set: &RuleSet, rule: &Rule) { + // Record negation. + self.expansion.negated.push(rule.id); + + // Collect the constraints required. + let mut constraints = Vec::new(); + for i in 0..rule_set.bindings.len() { + let binding_id = i.try_into().unwrap(); + if let Some(constraint) = rule.get_constraint(binding_id) { + let expansion_binding_id = self.add_binding(rule_set, binding_id); + constraints.push(Constrain::Match(expansion_binding_id, constraint)); + } + } + + // Require their negation. + self.expansion.constrain(Constrain::NotAll(constraints)); + + // TODO(mbm): negation of equality constraints + } + + fn add_binding(&mut self, rule_set: &RuleSet, binding_id: BindingId) -> BindingId { + // Check if it has already been added. + if self.import_reindex.is_mapped(binding_id) { + return self.import_reindex.id(binding_id); + } + + // Add dependencies first. + let binding = &rule_set.bindings[binding_id.index()]; + for source in binding.sources() { + self.add_binding(rule_set, *source); + } + + // Reindex this binding. + let reindexed = self.import_reindex.binding(binding); + + // Insert into expansion bindings list. + let expansion_binding_id = self.expansion.push_binding(reindexed); + + // Record binding mapping. + self.import_reindex.map(binding_id, expansion_binding_id); + expansion_binding_id + } +} + +/// Reindexing binding IDs. +struct Reindex { + to: HashMap, +} + +impl Reindex { + fn new() -> Self { + Self { to: HashMap::new() } + } + + fn is_mapped(&self, binding_id: BindingId) -> bool { + self.to.contains_key(&binding_id) + } + + fn map(&mut self, from: BindingId, to: BindingId) { + self.to.insert(from, to); + } + + fn id(&self, binding_id: BindingId) -> BindingId { + self.to.get(&binding_id).copied().unwrap_or(binding_id) + } + + fn ids(&self, binding_ids: &[BindingId]) -> Box<[BindingId]> { + binding_ids + .iter() + .map(|binding_id| self.id(*binding_id)) + .collect() + } + + fn binding(&self, binding: &Binding) -> Binding { + match binding { + Binding::Argument { .. } + | Binding::ConstInt { .. } + | Binding::ConstBool { .. } + | Binding::ConstPrim { .. } => binding.clone(), + + Binding::Extractor { term, parameter } => Binding::Extractor { + term: *term, + parameter: self.id(*parameter), + }, + + Binding::Constructor { + term, + parameters, + instance, + } => Binding::Constructor { + term: *term, + parameters: self.ids(parameters), + instance: *instance, + }, + + Binding::MakeVariant { + ty, + variant, + fields, + } => Binding::MakeVariant { + ty: *ty, + variant: *variant, + fields: self.ids(fields), + }, + + Binding::MatchVariant { + source, + variant, + field, + } => Binding::MatchVariant { + source: self.id(*source), + variant: *variant, + field: *field, + }, + + Binding::MakeStruct { ty, fields } => Binding::MakeStruct { + ty: *ty, + fields: self.ids(fields), + }, + + Binding::ExtractStruct { source, field } => Binding::ExtractStruct { + source: self.id(*source), + field: *field, + }, + + Binding::MakeSome { inner } => Binding::MakeSome { + inner: self.id(*inner), + }, + + Binding::MatchSome { source } => Binding::MatchSome { + source: self.id(*source), + }, + + Binding::MatchTuple { source, field } => Binding::MatchTuple { + source: self.id(*source), + field: *field, + }, + + Binding::Iterator { .. } => unimplemented!("iterator bindings not supported"), + } + } +} diff --git a/cranelift/isle/veri/veri/src/explorer.rs b/cranelift/isle/veri/veri/src/explorer.rs new file mode 100644 index 000000000000..c1d6d39b888f --- /dev/null +++ b/cranelift/isle/veri/veri/src/explorer.rs @@ -0,0 +1,807 @@ +use anyhow::{Result, bail}; +use cranelift_isle::{ + lexer::Pos, + sema::{RuleId, TermId, TypeId}, + trie_again::BindingId, +}; + +use crate::{ + debug::{binding_string, constrain_string}, + expand::{Chaining, Expansion}, + program::Program, + trie::{BindingType, binding_type}, +}; +use std::{ + fs::File, + io::{self, Write}, + path::{Component, Path, PathBuf}, +}; + +pub struct ExplorerWriter<'a> { + prog: &'a Program, + chaining: &'a Chaining<'a>, + expansions: &'a Vec, + + root: std::path::PathBuf, + base: std::path::PathBuf, + graphs: bool, + dev: bool, +} + +impl<'a> ExplorerWriter<'a> { + pub fn new( + root: std::path::PathBuf, + prog: &'a Program, + chaining: &'a Chaining<'a>, + expansions: &'a Vec, + ) -> Self { + Self { + prog, + chaining, + expansions, + root, + base: PathBuf::new(), + graphs: false, + dev: true, // TODO(mbm): configurable dev mode + } + } + + pub fn enable_graphs(&mut self) { + self.graphs = true; + } + + pub fn write(&mut self) -> Result<()> { + self.init()?; + self.write_assets()?; + self.write_index()?; + self.write_files()?; + self.write_types()?; + self.write_terms()?; + self.write_rules()?; + self.write_expansions()?; + Ok(()) + } + + fn init(&self) -> Result<()> { + std::fs::create_dir_all(&self.root)?; + Ok(()) + } + + fn write_assets(&mut self) -> Result<()> { + // In development mode, setup a symlink. + if self.dev { + let crate_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // TODO(mbm): platform-independent symlink + let original = crate_root.join("src/assets"); + let link_path = self.abs(&self.assets_dir()); + std::os::unix::fs::symlink(original, link_path)?; + return Ok(()); + } + + // CSS. + let style_css = include_bytes!("./assets/style.css"); + let mut output = self.create(&self.style_path())?; + output.write_all(style_css)?; + + Ok(()) + } + + fn write_index(&mut self) -> Result<()> { + let mut output = self.create(&PathBuf::from("index.html"))?; + self.header(&mut output, "ISLE Explorer")?; + writeln!( + output, + r#" +

+
  • Files
  • +
  • Types
  • +
  • Terms
  • +
  • Rules
  • +
  • Expansions
  • +
    + "#, + files_href = self.link(&self.file_dir()), + types_href = self.link(&self.types_dir()), + terms_href = self.link(&self.terms_dir()), + rules_href = self.link(&self.rules_dir()), + expansions_href = self.link(&self.expansions_dir()), + )?; + self.footer(&mut output)?; + Ok(()) + } + + fn write_files(&mut self) -> Result<()> { + self.write_files_index()?; + for id in 0..self.prog.files.file_names.len() { + self.write_file(id)?; + } + Ok(()) + } + + fn write_files_index(&mut self) -> Result<()> { + let mut output = self.create(&self.file_dir().join("index.html"))?; + self.header(&mut output, "Files")?; + + // Files. + writeln!(output, "
      ")?; + for (id, filename) in self.prog.files.file_names.iter().enumerate() { + writeln!( + output, + r#"
    • {filename}
    • "#, + link = self.link(&self.file_path(id)), + )?; + } + writeln!(output, "
    ")?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_file(&mut self, id: usize) -> Result<()> { + let mut output = self.create(&self.file_path(id))?; + + // Header. + let filename = &self.prog.files.file_names[id]; + let title = format!("File: {filename}"); + self.header(&mut output, &title)?; + + // Source code. + let file_text = &self.prog.files.file_texts[id]; + + writeln!(&mut output, "
    ")?;
    +        for (i, line) in file_text.lines().enumerate() {
    +            let n = i + 1;
    +            writeln!(
    +                &mut output,
    +                r#"{line}"#,
    +                fragment = self.line_url_fragment(n)
    +            )?;
    +        }
    +        writeln!(&mut output, "
    ")?; + + // Footer. + self.footer(&mut output)?; + + Ok(()) + } + + fn write_types(&mut self) -> Result<()> { + let mut output = self.create(&self.types_dir().join("index.html"))?; + self.header(&mut output, "Types")?; + + // Types. + writeln!( + output, + r#" + + + + + + + + + + + "# + )?; + for ty in &self.prog.tyenv.types { + writeln!(output, "")?; + writeln!(output, r#""#, id = ty.id().index())?; + + // Name. + writeln!(output, r"", name = ty.name(&self.prog.tyenv))?; + + // Location. + if let Some(pos) = ty.pos() { + writeln!(output, "", pos = self.pos(pos))?; + } else { + writeln!(output, "")?; + } + + // Model. + if let Some(model) = self.prog.specenv.type_model.get(&ty.id()) { + writeln!(output, "")?; + } else { + writeln!(output, "")?; + } + + writeln!(output, "")?; + } + writeln!( + output, + r#" + +
    #NameLocationModel
    {id}{name}{pos}builtin{model}
    + "# + )?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_terms(&mut self) -> Result<()> { + let mut output = self.create(&self.terms_dir().join("index.html"))?; + self.header(&mut output, "Terms")?; + + // Terms. + let term_ids = (0..self.prog.termenv.terms.len()).map(TermId); + self.write_terms_list(&mut output, term_ids)?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_terms_list( + &self, + output: &mut dyn Write, + term_ids: impl Iterator, + ) -> Result<()> { + writeln!( + output, + r#" + + + + + + + + + + + "# + )?; + for term_id in term_ids { + let term = self.prog.term(term_id); + + writeln!(output, "")?; + writeln!(output, r#""#, id = term.id.index())?; + + // Name. + writeln!( + output, + r"", + name = self.prog.term_name(term.id) + )?; + + // Location. + writeln!(output, "", pos = self.pos(term.decl_pos))?; + + // Spec. + if let Some(spec) = self.prog.specenv.term_spec.get(&term.id) { + writeln!(output, "", pos = self.pos(spec.pos))?; + } else if self.chaining.should_chain(term_id) { + writeln!(output, "")?; + } else { + writeln!(output, "")?; + } + + writeln!(output, "")?; + } + writeln!( + output, + r#" + +
    #NameLocationSpec
    {id}{name}{pos}{pos}chained
    + "# + )?; + Ok(()) + } + + fn write_rules(&mut self) -> Result<()> { + let mut output = self.create(&self.rules_dir().join("index.html"))?; + self.header(&mut output, "Rules")?; + + // Rules. + let rule_ids = (0..self.prog.termenv.rules.len()).map(RuleId); + self.write_rules_list(&mut output, rule_ids)?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_rules_list( + &self, + output: &mut dyn Write, + rule_ids: impl Iterator, + ) -> Result<()> { + writeln!( + output, + r#" + + + + + + + + + "# + )?; + + for rule_id in rule_ids { + writeln!(output, "")?; + writeln!(output, r#""#, id = rule_id.index())?; + writeln!( + output, + "", + rule_ref = self.rule_ref(rule_id) + )?; + writeln!(output, "")?; + } + + writeln!( + output, + r#" + +
    #Identifier
    {id}{rule_ref}
    + "# + )?; + Ok(()) + } + + fn write_expansions(&mut self) -> Result<()> { + self.write_expansions_index()?; + for (id, expansion) in self.expansions.iter().enumerate() { + self.write_expansion(id, expansion)?; + } + Ok(()) + } + + fn write_expansions_index(&mut self) -> Result<()> { + let mut output = self.create(&self.expansions_dir().join("index.html"))?; + self.header(&mut output, "Expansions")?; + + // Expansions. + writeln!( + output, + r#" + + + + + + + + + + + "# + )?; + for (id, expansion) in self.expansions.iter().enumerate() { + writeln!(output, "")?; + + // ID + writeln!( + output, + r#""#, + link = self.link(&self.expansion_path(id)) + )?; + + // Root + writeln!( + output, + "", + term_ref = self.term_ref(expansion.term) + )?; + + // First Rule + let rule_id = expansion + .rules + .first() + .expect("expansion must have at least one rule"); + writeln!( + output, + "", + rule_ref = self.rule_ref(*rule_id) + )?; + + // Tags + let mut tags: Vec = expansion.tags(self.prog).iter().cloned().collect(); + tags.sort(); + writeln!(output, "", tags = tags.join(", "))?; + + writeln!(output, "")?; + } + writeln!( + output, + r#" + +
    #RootFirst RuleTags
    #{id}{term_ref}{rule_ref}{tags}
    + "# + )?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_expansion(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + self.write_expansion_index(id, expansion)?; + if self.graphs { + self.write_expansion_graph(id, expansion)?; + } + Ok(()) + } + + fn write_expansion_index(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + let mut output = self.create(&self.expansion_path(id))?; + + // Header. + let title = format!("Expansion: #{id}"); + self.header(&mut output, &title)?; + + // Term. + writeln!( + output, + "

    Term: {term_ref}

    ", + term_ref = self.term_ref(expansion.term) + )?; + + // Rules + writeln!(output, "

    Rules

    ")?; + self.write_rules_list(&mut output, expansion.rules.iter().copied())?; + + // Negated Rules + if !expansion.negated.is_empty() { + writeln!(output, "

    Negated

    ")?; + self.write_rules_list(&mut output, expansion.negated.iter().copied())?; + } + + // Terms + writeln!(output, "

    Terms

    ")?; + let terms = expansion.terms(self.prog); + self.write_terms_list(&mut output, terms.into_iter())?; + + // Bindings + writeln!(output, "

    Bindings

    ")?; + if self.graphs { + writeln!( + output, + r#"

    Graph: SVG, DOT.

    "#, + svg_href = self.link(&self.expansion_graph_svg_path(id)), + dot_href = self.link(&self.expansion_graph_dot_path(id)), + )?; + } + + writeln!( + output, + r#" + + + + + "# + )?; + if !expansion.equals.is_empty() { + writeln!(output, "")?; + } + writeln!( + output, + r#" + + + + + + "# + )?; + let lookup_binding = + |binding_id: BindingId| expansion.bindings[binding_id.index()].clone().unwrap(); + for (i, binding) in expansion.bindings.iter().enumerate() { + let id: BindingId = i.try_into().unwrap(); + if let Some(binding) = binding { + writeln!(output, "")?; + let ty = binding_type(binding, expansion.term, self.prog, lookup_binding); + + // ID + writeln!(output, "", id = id.index())?; + + // Equals + if let Some(eq) = expansion.equals.find(id) + && id != eq + { + write!(output, "", eq.index())?; + } + + // Type + writeln!(output, "", ty = self.binding_type(&ty))?; + + // Binding + writeln!( + output, + "", + binding = binding_string(binding, expansion.term, self.prog, lookup_binding) + )?; + + writeln!(output, "")?; + } + } + + // TODO(mbm): Parameters + // TODO(mbm): Result + + writeln!( + output, + r#" + +
    #=TypeBinding
    {id}= {}{ty}{binding}
    + "# + )?; + + // Constraints + writeln!(output, "

    Constraints

    ")?; + writeln!(output, "
      ")?; + for constrain in &expansion.constraints { + writeln!( + output, + "
    • {constrain}
    • ", + constrain = constrain_string(constrain, &self.prog.tyenv) + )?; + } + writeln!(output, "
    ")?; + + // Footer. + self.footer(&mut output)?; + + Ok(()) + } + + fn write_expansion_graph(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + self.write_expansion_graph_dot(id, expansion)?; + self.generate_expansion_graph_svg(id)?; + Ok(()) + } + + fn write_expansion_graph_dot(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + let mut output = self.create(&self.expansion_graph_dot_path(id))?; + + // Header. + writeln!(&mut output, "digraph {{")?; + writeln!(&mut output, "\tnode [shape=box, fontname=monospace];")?; + + // Binding nodes. + let lookup_binding = + |binding_id: BindingId| expansion.bindings[binding_id.index()].clone().unwrap(); + for (i, binding) in expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + writeln!( + &mut output, + "\tb{i} [label=\"{i}: {}\"];", + binding_string(binding, expansion.term, self.prog, lookup_binding) + )?; + } + } + + // Edges. + for (i, binding) in expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + for source in binding.sources() { + writeln!(&mut output, "\tb{i} -> b{j};", j = source.index())?; + } + } + } + + writeln!(&mut output, "}}")?; + + Ok(()) + } + + fn generate_expansion_graph_svg(&self, id: usize) -> Result<()> { + let dot_path = self.expansion_graph_dot_path(id); + let svg_path = self.expansion_graph_svg_path(id); + + // Invoke graphviz. + let status = std::process::Command::new("dot") + .current_dir(&self.root) + .arg("-Tsvg") + .arg("-o") + .arg(svg_path) + .arg(dot_path) + .status()?; + + if !status.success() { + bail!("dot exit status: {status}"); + } + + Ok(()) + } + + fn header(&self, output: &mut dyn Write, title: &str) -> io::Result<()> { + write!( + output, + r#" + + + + + {title} + + + +
    +

    {title}

    + "#, + style_path = self.link(&self.style_path()) + ) + } + + fn footer(&self, output: &mut dyn Write) -> io::Result<()> { + write!( + output, + r#" +
    + + + "# + ) + } + + fn binding_type(&self, ty: &BindingType) -> String { + match ty { + BindingType::Base(type_id) => self.type_ref(*type_id), + BindingType::Option(inner) => format!("Option({})", self.binding_type(inner)), + BindingType::Tuple(inners) => format!( + "({inners})", + inners = inners + .iter() + .map(|inner| self.binding_type(inner)) + .collect::>() + .join(", ") + ), + } + } + + fn type_ref(&self, type_id: TypeId) -> String { + let ty = self.prog.ty(type_id); + format!( + r#"{name}"#, + href = self.pos_href(ty.pos().expect("expected position")), + name = self.prog.type_name(ty.id()) + ) + } + + fn term_ref(&self, term_id: TermId) -> String { + let term = self.prog.term(term_id); + format!( + r#"{name}"#, + href = self.pos_href(term.decl_pos), + name = self.prog.term_name(term_id) + ) + } + + fn rule_ref(&self, rule_id: RuleId) -> String { + let rule = self.prog.rule(rule_id); + format!( + r#"{identifier}"#, + href = self.pos_href(rule.pos), + identifier = rule.identifier(&self.prog.tyenv, &self.prog.files) + ) + } + + fn pos(&self, pos: Pos) -> String { + if pos.is_unknown() { + "<unknown>".to_string() + } else { + format!( + r#"{loc}"#, + href = self.pos_href(pos), + loc = self.loc(pos) + ) + } + } + + fn loc(&self, pos: Pos) -> String { + let path = PathBuf::from(&self.prog.files.file_names[pos.file]); + format!( + "{}:{}", + path.file_name().unwrap().to_string_lossy(), + self.line(pos) + ) + } + + fn pos_href(&self, pos: Pos) -> String { + format!( + "{}#{}", + self.link(&self.file_path(pos.file)), + self.line_url_fragment(self.line(pos)) + ) + } + + fn line_url_fragment(&self, n: usize) -> String { + format!("L{n}") + } + + fn line(&self, pos: Pos) -> usize { + self.prog + .files + .file_line_map(pos.file) + .unwrap() + .line(pos.offset) + } + + fn types_dir(&self) -> PathBuf { + PathBuf::from("type") + } + + fn terms_dir(&self) -> PathBuf { + PathBuf::from("term") + } + + fn rules_dir(&self) -> PathBuf { + PathBuf::from("rule") + } + + fn expansions_dir(&self) -> PathBuf { + PathBuf::from("expansion") + } + + fn expansion_dir(&self, id: usize) -> PathBuf { + self.expansions_dir().join(id.to_string()) + } + + fn expansion_path(&self, id: usize) -> PathBuf { + self.expansion_dir(id).join("index.html") + } + + fn expansion_graph_dot_path(&self, id: usize) -> PathBuf { + self.expansion_dir(id).join("graph.dot") + } + + fn expansion_graph_svg_path(&self, id: usize) -> PathBuf { + self.expansion_dir(id).join("graph.svg") + } + + fn file_dir(&self) -> PathBuf { + PathBuf::from("file") + } + + fn file_path(&self, id: usize) -> PathBuf { + self.file_dir().join(format!("{id}.html")) + } + + fn assets_dir(&self) -> PathBuf { + PathBuf::from("assets") + } + + fn asset_path(&self, name: &str) -> PathBuf { + self.assets_dir().join(name) + } + + fn style_path(&self) -> PathBuf { + self.asset_path("style.css") + } + + fn abs(&self, path: &Path) -> PathBuf { + self.root.join(path) + } + + fn link(&self, path: &Path) -> String { + assert!(path.is_relative()); + assert!(self.base.is_relative()); + + let mut comps = Vec::new(); + for _ in self.base.components() { + comps.push(Component::ParentDir); + } + comps.extend(path.components()); + let rel: PathBuf = comps.iter().map(|c| c.as_os_str()).collect(); + rel.display().to_string() + } + + fn create(&mut self, path: &Path) -> io::Result { + // Path expected to be relative to site root. + assert!(path.is_relative()); + + // Update base directory for relative links. + self.base = path.parent().expect("should have parent path").into(); + + // Create the file, and any parent directories if necessary. + log::info!("create: {}", path.display()); + let path = self.abs(path); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent)?; + } + File::create(&path) + } +} diff --git a/cranelift/isle/veri/veri/src/lib.rs b/cranelift/isle/veri/veri/src/lib.rs new file mode 100644 index 000000000000..1d58ca862faf --- /dev/null +++ b/cranelift/isle/veri/veri/src/lib.rs @@ -0,0 +1,36 @@ +// TODO(mbm): declare_id is copied from ISLE crate. move it to a common location? +macro_rules! declare_id { + ( + $(#[$attr:meta])* + $name:ident + ) => { + $(#[$attr])* + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name(pub usize); + impl $name { + /// Get the index of this id. + pub fn index(self) -> usize { + self.0 + } + } + }; +} + +pub mod debug; +pub mod encoded; +pub mod expand; +pub mod explorer; +pub mod program; +pub mod reachability; +pub mod runner; +pub mod solver; +pub mod spec; +pub mod trie; +pub mod type_inference; +pub mod types; +pub mod veri; + +#[cfg(test)] +pub mod testing; + +include!(concat!(env!("OUT_DIR"), "/meta.rs")); diff --git a/cranelift/isle/veri/veri/src/program.rs b/cranelift/isle/veri/veri/src/program.rs new file mode 100644 index 000000000000..bd6552ee1f71 --- /dev/null +++ b/cranelift/isle/veri/veri/src/program.rs @@ -0,0 +1,192 @@ +use crate::spec::{self, SpecEnv}; +use crate::trie; +use anyhow::{Result, bail}; +use cranelift_isle::ast::{Def, Ident}; +use cranelift_isle::error::{self, Errors, Span}; +use cranelift_isle::files::Files; +use cranelift_isle::lexer::Pos; +use cranelift_isle::sema::{ + self, Rule, RuleId, Term, TermEnv, TermId, Type, TypeEnv, TypeId, VariantId, +}; +use cranelift_isle::trie_again::{Overlap, RuleSet}; +use cranelift_isle::{lexer, parser}; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +pub struct Program { + pub files: Arc, + pub tyenv: TypeEnv, + pub termenv: TermEnv, + pub specenv: SpecEnv, + pub overlaps: HashMap>, +} + +impl Program { + pub fn from_files( + paths: &Vec, + expand_internal_extractors: bool, + ) -> Result { + let files = match Files::from_paths(paths, Default::default()) { + Ok(files) => files, + Err((path, err)) => { + bail!(Errors::from_io( + err, + format!("cannot read file {}", path.display()), + )) + } + }; + + let files = Arc::new(files); + + let mut defs = Vec::new(); + for (file, src) in files.file_texts.iter().enumerate() { + let lexer = match lexer::Lexer::new(file, src) { + Ok(lexer) => lexer, + Err(err) => bail!(Errors::new(vec![err], files)), + }; + + match parser::parse(lexer) { + Ok(mut ds) => defs.append(&mut ds), + Err(err) => bail!(Errors::new(vec![err], files)), + } + } + + let mut tyenv = match sema::TypeEnv::from_ast(&defs) { + Ok(type_env) => type_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + let termenv = match sema::TermEnv::from_ast(&mut tyenv, &defs, expand_internal_extractors) { + Ok(term_env) => term_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + let specenv = spec::SpecEnv::from_ast(&defs, &termenv, &tyenv)?; + + let overlaps = Self::build_overlaps(&defs, files.clone())?; + + Ok(Self { + files, + tyenv, + termenv, + specenv, + overlaps, + }) + } + + pub fn ty(&self, type_id: TypeId) -> &Type { + self.tyenv + .types + .get(type_id.index()) + .expect("invalid type id") + } + + pub fn type_name(&self, type_id: TypeId) -> &str { + self.ty(type_id).name(&self.tyenv) + } + + pub fn term(&self, term_id: TermId) -> &Term { + self.termenv + .terms + .get(term_id.index()) + .expect("invalid term id") + } + + pub fn term_name(&self, term_id: TermId) -> &str { + let term = self.term(term_id); + &self.tyenv.syms[term.name.index()] + } + + pub fn get_variant_term(&self, ty: TypeId, variant: VariantId) -> TermId { + self.termenv.get_variant_term(&self.tyenv, ty, variant) + } + + pub fn rule(&self, rule_id: RuleId) -> &Rule { + self.termenv + .rules + .get(rule_id.index()) + .expect("invalid rule id") + } + + pub fn rule_identifer(&self, rule_id: RuleId) -> String { + let rule = self.rule(rule_id); + rule.identifier(&self.tyenv, &self.files) + } + + pub fn rules_by_term(&self) -> HashMap> { + let mut rules: HashMap> = HashMap::new(); + for rule in &self.termenv.rules { + rules.entry(rule.root_term).or_default().push(rule.id); + } + rules + } + + pub fn get_rule_by_identifier(&self, id: &str) -> Option<&Rule> { + self.termenv + .rules + .iter() + .find(|r| r.identifier(&self.tyenv, &self.files) == id) + } + + pub fn get_term_by_name(&self, name: &str) -> Option { + let sym = Ident(name.to_string(), Pos::default()); + self.termenv.get_term_by_name(&self.tyenv, &sym) + } + + pub fn build_trie(&self) -> Result, Errors> { + trie::build_trie(&self.termenv, self.files.clone()) + } + + pub(crate) fn error_at_pos(&self, pos: Pos, msg: impl Into) -> Errors { + // In order to piggy back off the existing diagnostic error reporting in + // ISLE, we shoehorn our error type into one of the existing error + // categories. + // + // TODO(mbm): cleaner positional error reporting for the verifier + let err = error::Error::TypeError { + msg: msg.into(), + span: Span::new_single(pos), + }; + Errors::new(vec![err], self.files.clone()) + } + + fn build_overlaps(defs: &[Def], files: Arc) -> Result>> { + // Overlap checking relies on term environment constructed with internal + // extractor expansion enabled, so we need to generate it again. + let mut tyenv = match sema::TypeEnv::from_ast(defs) { + Ok(type_env) => type_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + let expand_internal_extractors = true; + let termenv = match sema::TermEnv::from_ast(&mut tyenv, defs, expand_internal_extractors) { + Ok(term_env) => term_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + // Check all pairs of rules for overlap. + let term_rule_sets = trie::build_trie(&termenv, files.clone())?; + let mut overlaps: HashMap> = HashMap::new(); + for (_, rule_set) in &term_rule_sets { + for rule in &rule_set.rules { + for other in &rule_set.rules { + // Ignore same or higher priority rules. + if other.prio <= rule.prio { + continue; + } + + // Check for overlap. + let overlap = rule.may_overlap(other); + if overlap == Overlap::No { + continue; + } + + // Record overlap. + overlaps.entry(rule.id).or_default().insert(other.id); + } + } + } + + Ok(overlaps) + } +} diff --git a/cranelift/isle/veri/veri/src/reachability.rs b/cranelift/isle/veri/veri/src/reachability.rs new file mode 100644 index 000000000000..4f8060f56e31 --- /dev/null +++ b/cranelift/isle/veri/veri/src/reachability.rs @@ -0,0 +1,69 @@ +use std::collections::{HashMap, HashSet}; + +use cranelift_isle::{ + sema::TermId, + trie_again::{Binding, RuleSet}, +}; + +pub struct Reachability { + reachable: HashMap>, +} + +impl Reachability { + pub fn build(term_rule_sets: &HashMap) -> Self { + let mut reachable = HashMap::new(); + for term_id in term_rule_sets.keys() { + reachable.insert(*term_id, search(*term_id, term_rule_sets)); + } + Self { reachable } + } + + /// Set of terms reachable from the the given source. + pub fn reachable(&self, source: TermId) -> &HashSet { + &self.reachable[&source] + } + + /// Report whether the term is included in a cycle. + pub fn is_cyclic(&self, term_id: TermId) -> bool { + self.reachable(term_id).contains(&term_id) + } +} + +/// Search for all terms reachable from the source. +fn search(source: TermId, term_rule_sets: &HashMap) -> HashSet { + let mut reachable = HashSet::new(); + let mut stack = vec![source]; + + while let Some(term_id) = stack.pop() { + if !term_rule_sets.contains_key(&term_id) { + continue; + } + + let used = used_terms(&term_rule_sets[&term_id]); + for used_term_id in used { + if reachable.contains(&used_term_id) { + continue; + } + reachable.insert(used_term_id); + stack.push(used_term_id); + } + } + + reachable +} + +pub fn used_terms(rule_set: &RuleSet) -> HashSet { + rule_set + .bindings + .iter() + .filter_map(binding_used_term) + .collect() +} + +pub fn binding_used_term(binding: &Binding) -> Option { + match binding { + Binding::Constructor { term, .. } | Binding::Extractor { term, .. } => Some(*term), + // TODO(mbm): make variant uses the variant constructor term? + _ => None, + } +} diff --git a/cranelift/isle/veri/veri/src/runner.rs b/cranelift/isle/veri/veri/src/runner.rs new file mode 100644 index 000000000000..c51102118de6 --- /dev/null +++ b/cranelift/isle/veri/veri/src/runner.rs @@ -0,0 +1,1065 @@ +use std::{ + collections::{BTreeSet, HashMap}, + fs::File, + io::Write, + path::{Path, PathBuf}, + str::FromStr, + sync::Mutex, + time::{self, Duration}, +}; + +use anyhow::{Context as _, Error, Result, bail, format_err}; +use cranelift_isle::{ + sema::{Term, TermId}, + trie_again::RuleSet, +}; +use rayon::prelude::*; +use serde::Serialize; + +use crate::{ + BUILD_PROFILE, GIT_VERSION, + debug::{print_expansion, write_expansion}, + expand::{Chaining, Expander, Expansion}, + program::Program, + solver::{Applicability, Dialect, Solver, Verification}, + type_inference::{self, Assignment, Choice, type_constraint_system}, + veri::Conditions, +}; + +const LOG_DIR: &str = ".veriisle"; + +#[derive(Debug, Clone, Copy)] +pub enum SolverBackend { + Z3, + CVC5, +} + +impl SolverBackend { + fn prog(&self) -> &str { + match self { + SolverBackend::Z3 => "z3", + SolverBackend::CVC5 => "cvc5", + } + } + + fn all() -> Vec { + vec![SolverBackend::Z3, SolverBackend::CVC5] + } + + fn dialect(&self) -> Dialect { + match self { + SolverBackend::Z3 => Dialect::Z3, + SolverBackend::CVC5 => Dialect::SMTLIB2, + } + } + + fn args(&self, timeout: Duration) -> Vec { + match self { + SolverBackend::Z3 => vec![ + "-smt2".to_string(), + "-in".to_string(), + format!("-t:{}", timeout.as_millis()), + ], + SolverBackend::CVC5 => vec![ + "--incremental".to_string(), + "--print-success".to_string(), + format!("--tlimit-per={ms}", ms = timeout.as_millis()), + "-".to_string(), + ], + } + } +} + +impl std::fmt::Display for SolverBackend { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.prog()) + } +} + +impl FromStr for SolverBackend { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + Ok(match s { + "z3" => SolverBackend::Z3, + "cvc5" => SolverBackend::CVC5, + _ => bail!("unknown solver backend"), + }) + } +} + +#[derive(Debug, Clone)] +pub enum ExpansionPredicate { + FirstRuleNamed, + Specified, + Tagged(String), + Root(String), + ContainsRule(String), + Not(Box), + And(Box, Box), +} + +impl FromStr for ExpansionPredicate { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(if let Some((p, q)) = s.split_once(',') { + ExpansionPredicate::And(Box::new(p.parse()?), Box::new(q.parse()?)) + } else if let Some(p) = s.strip_prefix("not:") { + ExpansionPredicate::Not(Box::new(p.parse()?)) + } else if s == "first-rule-named" { + ExpansionPredicate::FirstRuleNamed + } else if s == "specified" { + ExpansionPredicate::Specified + } else if let Some(tag) = s.strip_prefix("tag:") { + ExpansionPredicate::Tagged(tag.to_string()) + } else if let Some(term) = s.strip_prefix("root:") { + ExpansionPredicate::Root(term.to_string()) + } else if let Some(rule) = s.strip_prefix("rule:") { + ExpansionPredicate::ContainsRule(rule.to_string()) + } else { + bail!("invalid expansion predicate") + }) + } +} + +impl std::fmt::Display for ExpansionPredicate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExpansionPredicate::FirstRuleNamed => write!(f, "first-rule-named"), + ExpansionPredicate::Specified => write!(f, "specified"), + ExpansionPredicate::Tagged(tag) => write!(f, "tag:{tag}"), + ExpansionPredicate::Root(term) => write!(f, "root:{term}"), + ExpansionPredicate::ContainsRule(rule) => write!(f, "rule:{rule}"), + ExpansionPredicate::Not(p) => write!(f, "not:{p}"), + ExpansionPredicate::And(p, q) => write!(f, "{p},{q}"), + } + } +} + +impl ExpansionPredicate { + /// Describe, in natural English, the expansions this predicate matches. + fn describe(&self) -> String { + match self { + ExpansionPredicate::FirstRuleNamed => "whose first rule has a name".to_string(), + ExpansionPredicate::Specified => "marked as specified".to_string(), + ExpansionPredicate::Tagged(tag) => format!("tagged `{tag}`"), + ExpansionPredicate::Root(term) => format!("rooted at the term `{term}`"), + ExpansionPredicate::ContainsRule(rule) => { + format!("that use the rule `{rule}`") + } + ExpansionPredicate::Not(p) => format!("not {}", p.describe()), + ExpansionPredicate::And(p, q) => { + format!("{} and {}", p.describe(), q.describe()) + } + } + } +} + +#[derive(Debug, Clone)] +pub struct Filter { + include: bool, + predicate: ExpansionPredicate, +} + +impl Filter { + fn new(include: bool, predicate: ExpansionPredicate) -> Self { + Self { include, predicate } + } + + fn include(predicate: ExpansionPredicate) -> Self { + Self::new(true, predicate) + } + + fn exclude(predicate: ExpansionPredicate) -> Self { + Self::new(false, predicate) + } + + /// Describe this filter as a natural-English sentence, e.g. + /// "Excluding ISLE terms tagged `vector`." + pub fn describe(&self) -> String { + let verb = if self.include { + "Including" + } else { + "Excluding" + }; + format!("{verb} ISLE terms {}.", self.predicate.describe()) + } +} + +impl FromStr for Filter { + type Err = Error; + + fn from_str(s: &str) -> Result { + let (include, p) = if let Some(p) = s.strip_prefix("include:") { + (true, p) + } else if let Some(p) = s.strip_prefix("exclude:") { + (false, p) + } else { + (true, s) + }; + Ok(Filter::new(include, p.parse()?)) + } +} + +impl std::fmt::Display for Filter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let include = if self.include { "include" } else { "exclude" }; + write!( + f, + "{include}:{predicate}", + include = include, + predicate = self.predicate + ) + } +} + +#[derive(Debug, Clone)] +pub struct SolverRule { + predicate: ExpansionPredicate, + solver_backend: SolverBackend, +} + +impl SolverRule { + /// Build a rule that selects the solver backend for expansions with an + /// explicit `solver_` tag. + fn solver_tag(solver_backend: SolverBackend) -> Self { + let tag = format!("solver_{}", solver_backend); + Self { + predicate: ExpansionPredicate::Tagged(tag), + solver_backend, + } + } + + /// Build rules for explicit selection of all solver backends. + fn solver_tag_rules() -> Vec { + SolverBackend::all() + .iter() + .map(|backend| Self::solver_tag(*backend)) + .collect() + } +} + +impl FromStr for SolverRule { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + if let Some((backend, predicate)) = s.split_once('=') { + Ok(Self { + predicate: predicate.parse()?, + solver_backend: backend.parse()?, + }) + } else { + bail!("invalid solver rule") + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Verdict { + Inapplicable, + Success, + Unknown, + Failure, + ApplicabilityUnknown, +} + +#[derive(Serialize)] +pub struct VerifyReport { + pub verdict: Verdict, + + pub init_time: Duration, + pub applicable_time: Duration, + #[serde(skip_serializing_if = "Option::is_none")] + pub verify_time: Option, +} + +#[derive(Serialize)] +pub struct TypeInstantationReport { + pub choices: Vec, + pub verify: VerifyReport, + pub duration: Duration, +} + +#[derive(Serialize)] +pub struct ExpansionReport { + pub id: usize, + pub description: String, + pub root: String, + pub rules: Vec, + pub chained: Vec, + pub terms: Vec, + pub tags: Vec, + pub solver: String, + /// Count of type instantiations that failed at type inference. + pub failed_type_inference: usize, + /// Solver reports from type instantiations. + pub type_instantiations: Vec, + pub duration: Duration, +} + +impl ExpansionReport { + fn from_expansion(id: usize, expansion: &Expansion, prog: &Program) -> Result { + // Description + let description = expansion_description(expansion, prog)?; + + // Root term. + let root = prog.term_name(expansion.term).to_string(); + + // Tags + let mut tags: Vec<_> = expansion.tags(prog).iter().cloned().collect(); + tags.sort(); + + // Rules + let mut rules = Vec::new(); + let mut chained = BTreeSet::new(); + for rule_id in &expansion.rules { + let rule = prog.rule(*rule_id); + rules.push(rule.identifier(&prog.tyenv, &prog.files)); + + if rule.root_term != expansion.term { + let root_term = prog.term_name(rule.root_term); + if !chained.contains(&root_term) { + chained.insert(root_term); + } + } + } + + // Terms + let terms: BTreeSet<_> = expansion + .terms(prog) + .iter() + .map(|term_id| prog.term_name(*term_id)) + .collect(); + + Ok(Self { + id, + root, + description, + rules, + chained: chained.iter().map(ToString::to_string).collect(), + terms: terms.iter().map(ToString::to_string).collect(), + tags, + solver: Default::default(), + failed_type_inference: 0, + type_instantiations: Vec::new(), + duration: Default::default(), + }) + } +} + +#[derive(Serialize)] +pub struct TermMetadata { + pub name: String, + pub class: String, + pub has_spec: bool, + pub tags: Vec, +} + +impl TermMetadata { + fn from_term(term: &Term, prog: &Program) -> Self { + let name = prog.term_name(term.id).to_string(); + let class = Self::classify_term(term); + let has_spec = prog.specenv.has_spec(term.id); + + let tags_set = prog + .specenv + .term_tags + .get(&term.id) + .cloned() + .unwrap_or_default(); + let mut tags: Vec<_> = tags_set.iter().cloned().collect(); + tags.sort(); + + Self { + name, + class, + has_spec, + tags, + } + } + + fn from_prog(prog: &Program) -> Vec { + let mut terms = Vec::new(); + for term in &prog.termenv.terms { + terms.push(Self::from_term(term, prog)); + } + terms + } + + fn classify_term(term: &Term) -> String { + if term.is_enum_variant() { + return "enum_variant".to_string(); + } + + if term.has_external_constructor() || term.has_external_extractor() { + return "external".to_string(); + } + + if term.has_extractor() { + return "extractor".to_string(); + } + + assert!(term.has_constructor()); + + "constructor".to_string() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FailureKind { + Verification, + ApplicabilityUnknown, +} + +#[derive(Debug, Clone)] +pub struct FailureRecord { + pub kind: FailureKind, + pub expansion_id: usize, + pub description: String, + pub instantiation_index: usize, + pub failure_path: PathBuf, +} + +#[derive(Serialize)] +pub struct Report { + build_profile: String, + git_version: String, + args: Vec, + filters: Vec, + default_solver: String, + timeout: Duration, + duration: Duration, + num_threads: usize, + terms: Vec, + expansions: Vec, +} + +/// Runner orchestrates execution of the verification process over a set of +/// expansions. +pub struct Runner { + prog: Program, + term_rule_sets: HashMap, + + /// Optional single root term to scope expansion to. If `None`, expansion is + /// seeded from every term that has rules (all paths from all roots). + root_term: Option, + filters: Vec, + default_solver_backend: SolverBackend, + solver_rules: Vec, + timeout: Duration, + log_dir: PathBuf, + skip_solver: bool, + results_to_log_dir: bool, + debug: bool, +} + +impl Runner { + pub fn from_files(inputs: &Vec) -> Result { + let expand_internal_extractors = false; + let prog = Program::from_files(inputs, expand_internal_extractors)?; + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + Ok(Self { + prog, + term_rule_sets, + root_term: None, + filters: Vec::new(), + default_solver_backend: SolverBackend::CVC5, + solver_rules: Vec::new(), + timeout: Duration::from_secs(5), + log_dir: PathBuf::from(LOG_DIR), + results_to_log_dir: false, + skip_solver: false, + debug: false, + }) + } + + pub fn set_root_term(&mut self, term: &str) { + self.root_term = Some(term.to_string()); + } + + pub fn filter(&mut self, filter: Filter) { + self.filters.push(filter); + } + + pub fn filters(&mut self, filters: &[Filter]) { + self.filters.extend(filters.iter().cloned()); + } + + pub fn include_first_rule_named(&mut self) { + self.filters + .push(Filter::include(ExpansionPredicate::FirstRuleNamed)); + } + + pub fn skip_tag(&mut self, tag: &str) { + self.filters + .push(Filter::exclude(ExpansionPredicate::Tagged(tag.to_string()))); + } + + pub fn target_rule(&mut self, id: &str) -> Result<()> { + self.filters + .push(Filter::include(ExpansionPredicate::ContainsRule( + id.to_string(), + ))); + Ok(()) + } + + // Configure the default solver to use if no solver rules apply. + pub fn set_default_solver_backend(&mut self, solver_backend: SolverBackend) { + self.default_solver_backend = solver_backend; + } + + // Use the given solver backend for expansions that satisfy the given + // predicate. If multiple rules match, the earlier one is used. If none + // match, the default is used. + pub fn add_solver_rule(&mut self, solver_rule: SolverRule) { + self.solver_rules.push(solver_rule); + } + + // Configure rules for explicit solver selection based on `solver_` tags. + pub fn add_solver_tag_rules(&mut self) { + self.solver_rules.extend(SolverRule::solver_tag_rules()); + } + + pub fn set_timeout(&mut self, timeout: Duration) { + self.timeout = timeout; + } + + pub fn set_log_dir(&mut self, path: PathBuf) { + self.log_dir = path; + } + + pub fn set_results_to_log_dir(&mut self, enabled: bool) { + self.results_to_log_dir = enabled; + } + + pub fn skip_solver(&mut self, skip: bool) { + self.skip_solver = skip; + } + + pub fn debug(&mut self, debug: bool) { + self.debug = debug; + } + + pub fn run(&self) -> Result<()> { + // Clean log directory. + if self.log_dir.exists() { + std::fs::remove_dir_all(&self.log_dir)?; + } + + // Start timer. + let num_threads = rayon::current_num_threads(); + let start = time::Instant::now(); + + // Generate expansions. + // TODO(mbm): don't hardcode the expansion configuration + let chaining = Chaining::new(&self.prog, &self.term_rule_sets)?; + chaining.validate()?; + let mut expander = Expander::new(&self.prog, &self.term_rule_sets, chaining); + match &self.root_term { + // Scope expansion to a single explicitly configured root term. + Some(root_term) => expander.add_root_term_name(root_term)?, + // Default: seed an expansion at every term that has rules, a + // constructor, and an explicit specification. Terms without a spec + // are not verified standalone: they are assumed to be part of some + // real chain and are reached only by being chained (inlined) into a + // specified root's expansion. Sub-terms reachable from another root + // are deduplicated by `add_root`. + None => { + for &term_id in self.term_rule_sets.keys() { + if self.prog.term(term_id).has_constructor() + && self.prog.specenv.has_spec(term_id) + { + expander.add_root(term_id); + } + } + } + } + expander.set_prune_infeasible(true); + expander.expand(); + + // Process expansions. + let expansions = expander.expansions(); + log::info!("expansions: {n}", n = expansions.len()); + + let failures: Mutex> = Mutex::new(Vec::new()); + + let mut expansion_reports = expansions + .par_iter() + .enumerate() + .map(|(i, expansion)| -> Result> { + // Skip? + if !self.should_verify(expansion)? { + return Ok(None); + } + + // Verify + let expansion_log_dir = self.log_dir.join("expansions").join(format!("{:05}", i)); + let report = + self.verify_expansion(expansion, i, expansion_log_dir.clone(), &failures)?; + + Ok(Some(report)) + }) + .collect::>>()? + .into_iter() + .flatten() + .collect::>(); + + // End timer. + let duration = start.elapsed(); + + // Report failures, partitioned by kind. + let failures = failures.into_inner().unwrap(); + let (verification_failures, applicability_unknowns): (Vec<_>, Vec<_>) = failures + .into_iter() + .partition(|f| f.kind == FailureKind::Verification); + + let format_line = |failure: &FailureRecord| -> String { + format!( + "#{id}\t{description}\t(instantiation {inst})\t{path}", + id = failure.expansion_id, + description = failure.description, + inst = failure.instantiation_index, + path = failure.failure_path.display(), + ) + }; + + if !verification_failures.is_empty() { + let mut summary = Self::open_log_file(self.log_dir.clone(), "failures.out").ok(); + eprintln!( + "=== VERIFICATION FAILURES ({n}) ===", + n = verification_failures.len() + ); + for failure in &verification_failures { + let line = format_line(failure); + eprintln!("FAILURE {line}"); + if let Some(f) = summary.as_mut() { + let _ = writeln!(f, "{line}"); + } + } + log::warn!( + "verification failures: {n}", + n = verification_failures.len() + ); + } + + if !applicability_unknowns.is_empty() { + let mut summary = + Self::open_log_file(self.log_dir.clone(), "applicability_unknowns.out").ok(); + eprintln!( + "=== APPLICABILITY UNKNOWN ({n}) ===", + n = applicability_unknowns.len() + ); + for failure in &applicability_unknowns { + let line = format_line(failure); + eprintln!("APPLICABILITY UNKNOWN {line}"); + if let Some(f) = summary.as_mut() { + let _ = writeln!(f, "{line}"); + } + } + log::warn!( + "applicability unknowns: {n}", + n = applicability_unknowns.len() + ); + } + + // Prepare report + expansion_reports.sort_by_key(|a| a.id); + let terms = TermMetadata::from_prog(&self.prog); + let report = Report { + build_profile: BUILD_PROFILE.to_string(), + git_version: GIT_VERSION.to_string(), + args: std::env::args().collect(), + filters: self.filters.iter().map(ToString::to_string).collect(), + default_solver: self.default_solver_backend.prog().to_string(), + timeout: self.timeout, + num_threads, + duration, + terms, + expansions: expansion_reports, + }; + + // Write + let output = Self::open_log_file(self.log_dir.clone(), "report.json")?; + serde_json::to_writer_pretty(output, &report)?; + + // Verification failures are an overall error so that callers (the + // `veri` binary and tests) observe them via the returned `Result`. + if !verification_failures.is_empty() { + bail!("verification failures: {}", verification_failures.len()); + } + + Ok(()) + } + + fn should_verify(&self, expansion: &Expansion) -> Result { + let mut verdict = None; + for filter in self.filters.iter() { + verdict = self.eval_filter(filter, expansion)?.or(verdict); + } + // Default to including an expansion unless an `exclude` filter matches + // it. Because the last matching filter wins, an `include` filter can + // still carve an exception back out of a broader `exclude`. + Ok(verdict.unwrap_or(true)) + } + + fn eval_filter(&self, filter: &Filter, expansion: &Expansion) -> Result> { + Ok(if self.eval_predicate(&filter.predicate, expansion)? { + Some(filter.include) + } else { + None + }) + } + + fn eval_predicate( + &self, + predicate: &ExpansionPredicate, + expansion: &Expansion, + ) -> Result { + Ok(match predicate { + ExpansionPredicate::FirstRuleNamed => { + let rule_id = expansion + .rules + .first() + .ok_or(format_err!("expansion should have at least one rule"))?; + let rule = self.prog.rule(*rule_id); + rule.name.is_some() + } + ExpansionPredicate::Specified => expansion + .terms(&self.prog) + .iter() + .all(|term_id| self.prog.specenv.has_spec(*term_id)), + ExpansionPredicate::Tagged(tag) => { + let tags = expansion.tags(&self.prog); + tags.contains(tag) + } + ExpansionPredicate::Root(term) => self.prog.term_name(expansion.term) == term, + ExpansionPredicate::ContainsRule(identifier) => { + let rule = self + .prog + .get_rule_by_identifier(identifier) + .ok_or(format_err!("unknown rule '{identifier}'"))?; + expansion.rules.contains(&rule.id) + } + ExpansionPredicate::Not(p) => !self.eval_predicate(p, expansion)?, + ExpansionPredicate::And(p, q) => { + self.eval_predicate(p, expansion)? && self.eval_predicate(q, expansion)? + } + }) + } + + fn verify_expansion( + &self, + expansion: &Expansion, + id: usize, + log_dir: std::path::PathBuf, + failures: &Mutex>, + ) -> Result { + let description = expansion_description(expansion, &self.prog)?; + let start = time::Instant::now(); + + // Results output. + let mut output: Box = if self.results_to_log_dir { + log::info!("#{id}\t{description}"); + Box::new(Self::open_log_file(log_dir.clone(), "results.out")?) + } else { + Box::new(std::io::stdout()) + }; + + writeln!(output, "#{id}\t{description}")?; + if self.debug { + print_expansion(&self.prog, expansion); + } + + // Verification conditions. + let conditions = Conditions::from_expansion(expansion, &self.prog)?; + if self.debug { + conditions.pretty_print(&self.prog); + } + + // Type constraints. + let system = type_constraint_system(&conditions); + if self.debug { + system.pretty_print(); + } + + // Infer types. + let type_solver = type_inference::Solver::new(); + let solutions = type_solver.solve(&system); + + // Initialize report. + let mut report = ExpansionReport::from_expansion(id, expansion, &self.prog)?; + + // Select solver. + let solver_backend = self.select_solver_backend(expansion)?; + report.solver = solver_backend.to_string(); + + for (i, solution) in solutions.iter().enumerate() { + let start_solution = time::Instant::now(); + + // Show type assignment. + let mut choices = Vec::new(); + for choice in &solution.choices { + let choice = match choice { + Choice::TermInstantiation(term_id, sig) => { + format!("{term}{sig}", term = self.prog.term_name(*term_id)) + } + }; + writeln!(output, "\t{choice}")?; + choices.push(choice); + } + writeln!(output, "\t\ttype solution status = {}", solution.status)?; + if self.debug { + println!("type assignment:"); + solution.assignment.pretty_print(&conditions); + } + + match &solution.status { + type_inference::Status::Solved => (), + type_inference::Status::Inapplicable(conflict) => { + log::debug!( + "inapplicable type inference: {diagnostic}", + diagnostic = conflict.diagnostic(&conditions, &self.prog.files) + ); + report.failed_type_inference += 1; + continue; + } + type_inference::Status::Underconstrained => { + let underconstrained = solution.assignment.underconstrained(); + let mut diagnostic = format!( + "underconstrained type inference: could not infer a concrete type for \ + {n} expression(s) in expansion '{description}'. The following \ + expressions have ambiguous types; add type annotations or term \ + signatures to constrain them:", + n = underconstrained.len(), + ); + for x in underconstrained { + let tv = solution + .assignment + .assignment(x) + .expect("underconstrained expression must have a type value"); + let position = conditions + .pos + .get(&x) + .map(|pos| pos.pretty_print_line(&self.prog.files)) + .unwrap_or_else(|| "?".to_string()); + let expr = &conditions.exprs[x.index()]; + diagnostic.push_str(&format!( + "\n\t{position}: e{x} = {expr} (inferred type: {tv})", + x = x.index(), + )); + } + bail!(diagnostic) + } + type_inference::Status::TypeError(confict) => { + return Err(conditions.error_at_expr( + &self.prog, + confict.x, + confict.reason.clone(), + )); + } + } + + // Verify. + if self.skip_solver { + log::debug!("Skipping solver"); + continue; + } + + let solution_log_dir = log_dir.join(format!("{:03}", i)); + let verify_report = self + .verify_expansion_type_instantiation( + &conditions, + &solution.assignment, + solver_backend, + solution_log_dir, + &mut output, + expansion, + id, + &description, + i, + failures, + ) + .context(format!("verify expansion: {id}"))?; + + // Append to report. + let duration = start_solution.elapsed(); + report.type_instantiations.push(TypeInstantationReport { + choices, + verify: verify_report, + duration, + }); + } + + // End timer + report.duration = start.elapsed(); + + Ok(report) + } + + fn select_solver_backend(&self, expansion: &Expansion) -> Result { + for solver_rule in &self.solver_rules { + if self.eval_predicate(&solver_rule.predicate, expansion)? { + return Ok(solver_rule.solver_backend); + } + } + Ok(self.default_solver_backend) + } + + #[allow(clippy::too_many_arguments, reason = "verification code")] + fn verify_expansion_type_instantiation( + &self, + conditions: &Conditions, + assignment: &Assignment, + solver_backend: SolverBackend, + log_dir: std::path::PathBuf, + output: &mut dyn Write, + expansion: &Expansion, + expansion_id: usize, + description: &str, + instantiation_index: usize, + failures: &Mutex>, + ) -> Result { + let start = time::Instant::now(); + + // Solve. + let binary = solver_backend.prog(); + let args = solver_backend.args(self.timeout); + let replay_file = Self::open_log_file(log_dir.clone(), "solver.smt2")?; + let smt = easy_smt::ContextBuilder::new() + .solver(binary, &args) + .replay_file(Some(replay_file)) + .build()?; + + let mut solver = Solver::new(smt, &self.prog, conditions, assignment)?; + solver.set_dialect(solver_backend.dialect()); + solver.encode()?; + let init_time = start.elapsed(); + + // Applicability check. + let start = time::Instant::now(); + let applicability = solver.check_assumptions_feasibility()?; + let applicable_time = start.elapsed(); + + writeln!(output, "\t\tapplicability = {applicability}")?; + match applicability { + Applicability::Applicable => (), + Applicability::Inapplicable => { + return Ok(VerifyReport { + verdict: Verdict::Inapplicable, + init_time, + applicable_time, + verify_time: None, + }); + } + Applicability::Unknown => { + let unknown_path = log_dir.join("applicability_unknown.out"); + let mut unknown_file = + Self::open_log_file(log_dir.clone(), "applicability_unknown.out")?; + writeln!( + unknown_file, + "#{expansion_id}\t{description}\tinstantiation={instantiation_index}" + )?; + writeln!(unknown_file, "expansion:")?; + write_expansion(&mut unknown_file, &self.prog, expansion)?; + + writeln!( + output, + "\t\tapplicability unknown, written to {}", + unknown_path.display() + )?; + log::warn!( + "applicability unknown: #{expansion_id} {description} (expansion written to {})", + unknown_path.display() + ); + + failures.lock().unwrap().push(FailureRecord { + kind: FailureKind::ApplicabilityUnknown, + expansion_id, + description: description.to_string(), + instantiation_index, + failure_path: unknown_path, + }); + + return Ok(VerifyReport { + verdict: Verdict::ApplicabilityUnknown, + init_time, + applicable_time, + verify_time: None, + }); + } + }; + + // Verify. + let start = time::Instant::now(); + let verification = solver.check_verification_condition()?; + let verify_time = Some(start.elapsed()); + + writeln!(output, "\t\tverification = {verification}")?; + Ok(match verification { + Verification::Failure(model) => { + let failure_path = log_dir.join("failure.out"); + let mut failure_file = Self::open_log_file(log_dir.clone(), "failure.out")?; + writeln!( + failure_file, + "#{expansion_id}\t{description}\tinstantiation={instantiation_index}" + )?; + writeln!(failure_file, "expansion:")?; + write_expansion(&mut failure_file, &self.prog, expansion)?; + writeln!(failure_file, "model:")?; + conditions.write_model(&mut failure_file, &model, &self.prog)?; + + writeln!(output, "\t\tfailure written to {}", failure_path.display())?; + log::warn!( + "verification failure: #{expansion_id} {description} (model written to {})", + failure_path.display() + ); + + failures.lock().unwrap().push(FailureRecord { + kind: FailureKind::Verification, + expansion_id, + description: description.to_string(), + instantiation_index, + failure_path, + }); + + VerifyReport { + verdict: Verdict::Failure, + init_time, + applicable_time, + verify_time, + } + } + Verification::Success => VerifyReport { + verdict: Verdict::Success, + init_time, + applicable_time, + verify_time, + }, + Verification::Unknown => VerifyReport { + verdict: Verdict::Unknown, + init_time, + applicable_time, + verify_time, + }, + }) + } + + fn open_log_file>(log_dir: std::path::PathBuf, name: P) -> Result { + std::fs::create_dir_all(&log_dir)?; + let path = log_dir.join(name); + let file = File::create(&path)?; + Ok(file) + } +} + +/// Human-readable description of an expansion. +fn expansion_description(expansion: &Expansion, prog: &Program) -> Result { + let rule_id = expansion + .rules + .first() + .ok_or(format_err!("expansion should have at least one rule"))?; + let rule = prog.rule(*rule_id); + Ok(rule.identifier(&prog.tyenv, &prog.files)) +} diff --git a/cranelift/isle/veri/veri/src/solver.rs b/cranelift/isle/veri/veri/src/solver.rs new file mode 100644 index 000000000000..0b8f526516db --- /dev/null +++ b/cranelift/isle/veri/veri/src/solver.rs @@ -0,0 +1,1041 @@ +use std::{cmp::Ordering, collections::HashSet, iter::zip}; + +use anyhow::{Context as _, Error, Result, bail, format_err}; +use easy_smt::{Context, Response, SExpr, SExprData}; +use num_bigint::BigUint; +use num_traits::Num as _; + +use crate::{ + program::Program, + type_inference::Assignment, + types::{Const, Type, Width}, + veri::{Conditions, Expr, ExprId, Model}, +}; + +use crate::encoded::cls::*; +use crate::encoded::clz::*; +use crate::encoded::popcnt::*; +use crate::encoded::rev::*; + +#[derive(Debug, PartialEq, Eq)] +pub enum Applicability { + Applicable, + Inapplicable, + Unknown, +} + +impl std::fmt::Display for Applicability { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(match self { + Applicability::Applicable => "applicable", + Applicability::Inapplicable => "inapplicable", + Applicability::Unknown => "unknown", + }) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Verification { + Success, + Failure(Model), + Unknown, +} + +impl std::fmt::Display for Verification { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(match self { + Verification::Success => "success", + Verification::Failure(_) => "failure", + Verification::Unknown => "unknown", + }) + } +} + +enum RotationDirection { + Left, + Right, +} + +static UNSPECIFIED_SORT: &str = "Unspecified"; +static UNIT_SORT: &str = "Unit"; + +static ROUND_NEAREST_TIES_TO_EVEN: &str = "roundNearestTiesToEven"; +static ROUND_TOWARD_ZERO: &str = "roundTowardZero"; +static ROUND_TOWARD_POSITIVE: &str = "roundTowardPositive"; +static ROUND_TOWARD_NEGATIVE: &str = "roundTowardNegative"; +static ROUNDING_MODE: &str = ROUND_NEAREST_TIES_TO_EVEN; + +/// SMT Dialect. +#[derive(Default, Debug, Clone, Copy)] +pub enum Dialect { + /// SMT-LIB2 standard. + #[default] + SMTLIB2, + /// SMT-LIB2 with Z3 extensions. + Z3, +} + +pub struct Solver<'a> { + smt: Context, + dialect: Dialect, + prog: &'a Program, + conditions: &'a Conditions, + assignment: &'a Assignment, + tmp_idx: usize, + + /// Widths for which the deterministic `fp.sqrt` uninterpreted function has + /// already been declared (see [`Solver::fp_sqrt`]). + sqrt_uf_widths: HashSet, +} + +impl Drop for Solver<'_> { + fn drop(&mut self) { + // Attempt clean exit. + let _ = self.exit(); + } +} + +impl<'a> Solver<'a> { + pub fn new( + smt: Context, + prog: &'a Program, + conditions: &'a Conditions, + assignment: &'a Assignment, + ) -> Result { + let mut solver = Self { + smt, + dialect: Dialect::default(), + prog, + conditions, + assignment, + tmp_idx: 0, + sqrt_uf_widths: HashSet::new(), + }; + solver.prelude()?; + Ok(solver) + } + + pub fn set_dialect(&mut self, dialect: Dialect) { + self.dialect = dialect; + } + + fn prelude(&mut self) -> Result<()> { + // Set logic. Required for some SMT solvers. + self.smt.set_logic("ALL")?; + + // Declare sorts for special-case types. + self.smt.declare_sort(UNSPECIFIED_SORT, 0)?; + self.smt.declare_sort(UNIT_SORT, 0)?; + + Ok(()) + } + + pub fn encode(&mut self) -> Result<()> { + // Expressions + for (i, expr) in self.conditions.exprs.iter().enumerate() { + let x = ExprId(i); + self.declare_expr(x)?; + if !expr.is_variable() { + self.assign_expr(x, expr)?; + } + } + + Ok(()) + } + + pub fn check_assumptions_feasibility(&mut self) -> Result { + // Enter solver context frame. + self.smt.push()?; + + // Assumptions + let assumptions = self.all(&self.conditions.assumptions); + self.smt.assert(assumptions)?; + + // Check + let verdict = match self.check()? { + Response::Sat => Applicability::Applicable, + Response::Unsat => Applicability::Inapplicable, + Response::Unknown => Applicability::Unknown, + }; + + // Leave solver context frame. + self.smt.pop()?; + + Ok(verdict) + } + + pub fn check_verification_condition(&mut self) -> Result { + // Enter solver context frame. + self.smt.push()?; + + // Verification Condition + self.verification_condition()?; + + // Check + let verdict = match self.check()? { + Response::Sat => Verification::Failure(self.model()?), + Response::Unsat => Verification::Success, + Response::Unknown => Verification::Unknown, + }; + + // Leave solver context frame. + self.smt.pop()?; + + Ok(verdict) + } + + fn check(&mut self) -> Result { + // Send check-sat command. Prefer (check-sat-using default) for Z3. + let cmd = self.smt.list(match self.dialect { + Dialect::SMTLIB2 => vec![self.smt.atoms().check_sat], + Dialect::Z3 => vec![self.smt.atom("check-sat-using"), self.smt.atom("default")], + }); + + self.smt.raw_send(cmd)?; + + // Parse response. + let resp = self.smt.raw_recv()?; + let atoms = self.smt.atoms(); + if resp == atoms.sat { + Ok(Response::Sat) + } else if resp == atoms.unsat { + Ok(Response::Unsat) + } else if resp == atoms.unknown { + Ok(Response::Unknown) + } else { + bail!("bad solver check response: {}", self.smt.display(resp)) + } + } + + pub fn exit(&mut self) -> Result<()> { + // Send (exit) command. + let exit = self.smt.list(vec![self.smt.atom("exit")]); + self.smt.raw_send(exit)?; + + // Expect success response. + let resp = self.smt.raw_recv()?; + let atoms = self.smt.atoms(); + if resp != atoms.success { + bail!("bad solver exit: {}", self.smt.display(resp)) + } + Ok(()) + } + + pub fn model(&mut self) -> Result { + let xs: Vec<_> = (0..self.conditions.exprs.len()).map(ExprId).collect(); + let expr_atoms = xs.iter().map(|x| self.expr_atom(*x)).collect(); + let values = self.smt.get_value(expr_atoms)?; + let consts = values + .iter() + .map(|(_, v)| self.const_from_sexpr(*v)) + .collect::>>()?; + Ok(zip(xs, consts).collect()) + } + + fn declare_expr(&mut self, x: ExprId) -> Result<()> { + // Determine expression type value. + let tv = self.assignment.try_assignment(x)?; + + // Map to corresponding SMT2 type. + let sort = self.type_to_sort(&tv.ty())?; + + // Declare. + self.smt.declare_const(self.expr_name(x), sort)?; + + Ok(()) + } + + fn type_to_sort(&self, ty: &Type) -> Result { + match *ty { + Type::BitVector(Width::Bits(width)) => { + Ok(self.smt.bit_vec_sort(self.smt.numeral(width))) + } + Type::Int => Ok(self.smt.int_sort()), + Type::Bool => Ok(self.smt.bool_sort()), + Type::Unspecified => Ok(self.smt.atom(UNSPECIFIED_SORT)), + Type::Unit => Ok(self.smt.atom(UNIT_SORT)), + Type::Unknown | Type::BitVector(Width::Unknown) => { + bail!("no smt2 sort for non-concrete type {ty}") + } + } + } + + fn assign_expr(&mut self, x: ExprId, expr: &Expr) -> Result<()> { + let lhs = self.smt.atom(self.expr_name(x)); + let rhs = self + .expr_to_smt(expr) + .map_err(|err| self.error(x, err.to_string()))?; + Ok(self.smt.assert( + self.smt + .named(format!("expr{}", x.index()), self.smt.eq(lhs, rhs)), + )?) + } + + fn expr_to_smt(&mut self, expr: &Expr) -> Result { + match *expr { + Expr::Variable(_) => unreachable!("variables have no corresponding expression"), + Expr::Const(ref c) => Ok(self.constant(c)), + Expr::Not(x) => Ok(self.smt.not(self.expr_atom(x))), + Expr::And(x, y) => Ok(self.smt.and(self.expr_atom(x), self.expr_atom(y))), + Expr::Or(x, y) => Ok(self.smt.or(self.expr_atom(x), self.expr_atom(y))), + Expr::Imp(x, y) => Ok(self.smt.imp(self.expr_atom(x), self.expr_atom(y))), + Expr::Eq(x, y) => Ok(self.smt.eq(self.expr_atom(x), self.expr_atom(y))), + Expr::Lt(x, y) => Ok(self.smt.lt(self.expr_atom(x), self.expr_atom(y))), + Expr::Lte(x, y) => Ok(self.smt.lte(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUgt(x, y) => Ok(self.smt.bvugt(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUge(x, y) => Ok(self.smt.bvuge(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUlt(x, y) => Ok(self.smt.bvult(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUle(x, y) => Ok(self.smt.bvule(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSgt(x, y) => Ok(self.smt.bvsgt(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSge(x, y) => Ok(self.smt.bvsge(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSlt(x, y) => Ok(self.smt.bvslt(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSle(x, y) => Ok(self.smt.bvsle(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSaddo(x, y) => Ok(self.smt.list(vec![ + self.smt.atom("bvsaddo"), + self.expr_atom(x), + self.expr_atom(y), + ])), + Expr::BVNot(x) => Ok(self.smt.bvnot(self.expr_atom(x))), + Expr::Cls(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("cls semantics require known width")?; + let xe = self.expr_atom(x); + let id = x.index(); + match width { + 8 => Ok(cls8(&mut self.smt, xe, id)), + 16 => Ok(cls16(&mut self.smt, xe, id)), + 32 => Ok(cls32(&mut self.smt, xe, id)), + 64 => Ok(cls64(&mut self.smt, xe, id)), + _ => unimplemented!("unexpected CLS width"), + } + } + Expr::Clz(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("clz semantics require known width")?; + let xe = self.expr_atom(x); + let id: usize = x.index(); + match width { + 1 => Ok(clz1(&mut self.smt, xe, id)), + 8 => Ok(clz8(&mut self.smt, xe, id)), + 16 => Ok(clz16(&mut self.smt, xe, id)), + 32 => Ok(clz32(&mut self.smt, xe, id)), + 64 => Ok(clz64(&mut self.smt, xe, id)), + _ => unimplemented!("unexpected CLZ width"), + } + } + Expr::Rev(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("cls semantics require known width")?; + let xe = self.expr_atom(x); + let id: usize = x.index(); + match width { + 1 => Ok(rev1(&mut self.smt, xe, id)), + 8 => Ok(rev8(&mut self.smt, xe, id)), + 16 => Ok(rev16(&mut self.smt, xe, id)), + 32 => Ok(rev32(&mut self.smt, xe, id)), + 64 => Ok(rev64(&mut self.smt, xe, id)), + _ => unimplemented!("unexpected CLS width"), + } + } + Expr::Popcnt(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("popcnt semantics require known width")?; + let xe = self.expr_atom(x); + let id = x.index(); + match width { + 8 | 16 | 32 | 64 => Ok(popcnt(&mut self.smt, width, xe, id)), + _ => unimplemented!("unexpected Popcnt width"), + } + } + + Expr::Add(x, y) => Ok(self.smt.plus(self.expr_atom(x), self.expr_atom(y))), + Expr::Sub(x, y) => Ok(self.smt.sub(self.expr_atom(x), self.expr_atom(y))), + Expr::Mul(x, y) => Ok(self.smt.times(self.expr_atom(x), self.expr_atom(y))), + + Expr::BVNeg(x) => Ok(self.smt.bvneg(self.expr_atom(x))), + Expr::BVAdd(x, y) => Ok(self.smt.bvadd(self.expr_atom(x), self.expr_atom(y))), + Expr::BVOr(x, y) => Ok(self.smt.bvor(self.expr_atom(x), self.expr_atom(y))), + Expr::BVXor(x, y) => Ok(self.smt.bvxor(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSub(x, y) => Ok(self.smt.bvsub(self.expr_atom(x), self.expr_atom(y))), + Expr::BVMul(x, y) => Ok(self.smt.bvmul(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSDiv(x, y) => Ok(self.smt.list(vec![ + self.smt.atom("bvsdiv"), + self.expr_atom(x), + self.expr_atom(y), + ])), + Expr::BVUDiv(x, y) => Ok(self.smt.bvudiv(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSRem(x, y) => Ok(self.smt.bvsrem(self.expr_atom(x), self.expr_atom(y))), + Expr::BVURem(x, y) => Ok(self.smt.bvurem(self.expr_atom(x), self.expr_atom(y))), + Expr::BVAnd(x, y) => Ok(self.smt.bvand(self.expr_atom(x), self.expr_atom(y))), + Expr::BVShl(x, y) => Ok(self.smt.bvshl(self.expr_atom(x), self.expr_atom(y))), + Expr::BVLShr(x, y) => Ok(self.smt.bvlshr(self.expr_atom(x), self.expr_atom(y))), + Expr::BVAShr(x, y) => Ok(self.smt.bvashr(self.expr_atom(x), self.expr_atom(y))), + Expr::BVRotl(x, y) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("target of rotl expression should be a bit-vector of known width")?; + let xs = self.expr_atom(x); + let ys = self.expr_atom(y); + Ok(self.encode_rotate(RotationDirection::Left, xs, ys, width)) + } + Expr::BVRotr(x, y) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("target of rotr expression should be a bit-vector of known width")?; + let xs = self.expr_atom(x); + let ys = self.expr_atom(y); + Ok(self.encode_rotate(RotationDirection::Right, xs, ys, width)) + } + Expr::Conditional(c, t, e) => { + Ok(self + .smt + .ite(self.expr_atom(c), self.expr_atom(t), self.expr_atom(e))) + } + Expr::BVZeroExt(w, x) => self.bv_zero_ext(w, x), + Expr::BVSignExt(w, x) => self.bv_sign_ext(w, x), + Expr::BVConvTo(w, x) => self.bv_conv_to(w, x), + Expr::BVExtract(h, l, x) => Ok(self.extract(h, l, self.expr_atom(x))), + Expr::BVConcat(x, y) => Ok(self.smt.concat(self.expr_atom(x), self.expr_atom(y))), + Expr::Int2BV(w, x) => self.int_to_bv(w, x), + Expr::BV2Nat(x) => Ok(self + .smt + .list(vec![self.smt.atom("bv2nat"), self.expr_atom(x)])), + Expr::ToFP(w, x) => self.fp_from_expr(w, x, true), + Expr::ToFPUnsigned(w, x) => self.fp_from_expr(w, x, false), + Expr::ToFPFromFP(w, x) => self.fp_from_fp(w, x), + Expr::FPToUBV(w, x) => self.fp_to_bv(w, x, false), + Expr::FPToSBV(w, x) => self.fp_to_bv(w, x, true), + Expr::WidthOf(x) => self.width_of(x), + Expr::FPPositiveInfinity(x) => Ok(self.fp_value("+oo", x)?), + Expr::FPNegativeInfinity(x) => Ok(self.fp_value("-oo", x)?), + Expr::FPPositiveZero(x) => Ok(self.fp_value("+zero", x)?), + Expr::FPNegativeZero(x) => Ok(self.fp_value("-zero", x)?), + Expr::FPNaN(x) => Ok(self.fp_value("NaN", x)?), + Expr::FPEq(x, y) => Ok(self.fp_test("fp.eq", x, y)?), + Expr::FPNe(x, y) => { + let test_eq = self.fp_test("fp.eq", x, y)?; + Ok(self.smt.not(test_eq)) + } + Expr::FPLt(x, y) => Ok(self.fp_test("fp.lt", x, y)?), + Expr::FPGt(x, y) => Ok(self.fp_test("fp.gt", x, y)?), + Expr::FPLe(x, y) => Ok(self.fp_test("fp.leq", x, y)?), + Expr::FPGe(x, y) => Ok(self.fp_test("fp.geq", x, y)?), + Expr::FPAdd(x, y) => Ok(self.fp_rounding_binary("fp.add", x, y)?), + Expr::FPSub(x, y) => Ok(self.fp_rounding_binary("fp.sub", x, y)?), + Expr::FPMul(x, y) => Ok(self.fp_rounding_binary("fp.mul", x, y)?), + Expr::FPDiv(x, y) => Ok(self.fp_rounding_binary("fp.div", x, y)?), + Expr::FPMin(x, y) => Ok(self.fp_binary("fp.min", x, y)?), + Expr::FPMax(x, y) => Ok(self.fp_binary("fp.max", x, y)?), + Expr::FPNeg(x) => Ok(self.fp_unary("fp.neg", x)?), + Expr::FPCeil(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_TOWARD_POSITIVE, x)?) + } + Expr::FPFloor(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_TOWARD_NEGATIVE, x)?) + } + Expr::FPSqrt(x) => Ok(self.fp_sqrt(x)?), + Expr::FPTrunc(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_TOWARD_ZERO, x)?) + } + Expr::FPNearest(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_NEAREST_TIES_TO_EVEN, x)?) + } + Expr::FPIsZero(x) => Ok(self.fp_unary_predicate("fp.isZero", x)?), + Expr::FPIsInfinite(x) => Ok(self.fp_unary_predicate("fp.isInfinite", x)?), + Expr::FPIsNaN(x) => Ok(self.fp_unary_predicate("fp.isNaN", x)?), + Expr::FPIsNegative(x) => Ok(self.fp_unary_predicate("fp.isNegative", x)?), + Expr::FPIsPositive(x) => Ok(self.fp_unary_predicate("fp.isPositive", x)?), + } + } + + fn constant(&self, constant: &Const) -> SExpr { + match *constant { + Const::Bool(true) => self.smt.true_(), + Const::Bool(false) => self.smt.false_(), + Const::Int(v) => self.smt.numeral(v), + Const::BitVector(w, ref v) => self.smt.atom(format!("#b{v:0>w$b}")), + Const::Unspecified => unimplemented!("constant of unspecified type"), + } + } + + fn bv_zero_ext(&self, w: ExprId, x: ExprId) -> Result { + // TODO(mbm): dedupe logic with bv_sign_ext and bv_conv_to? + + // Destination width expression should have known integer value. + let dst: usize = self + .assignment + .try_int_value(w) + .context("destination width of zero_ext expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Expression type should be a bit-vector of known width. + let src = self + .assignment + .try_bit_vector_width(x) + .context("source of zero_ext expression should be a bit-vector of known width")?; + + // Build zero_extend expression. + let padding = dst + .checked_sub(src) + .ok_or(format_err!("cannot zero extend to smaller width"))?; + Ok(self.zero_extend(padding, self.expr_atom(x))) + } + + fn bv_sign_ext(&self, w: ExprId, x: ExprId) -> Result { + // TODO(mbm): dedupe logic with bv_conv_to? + + // Destination width expression should have known integer value. + let dst: usize = self + .assignment + .try_int_value(w) + .context("destination width of sign_ext expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Expression type should be a bit-vector of known width. + let src = self + .assignment + .try_bit_vector_width(x) + .context("source of sign_ext expression should be a bit-vector of known width")?; + + // Build sign_extend expression. + let padding = dst + .checked_sub(src) + .ok_or(format_err!("cannot sign extend to smaller width"))?; + Ok(self.sign_extend(padding, self.expr_atom(x))) + } + + fn bv_conv_to(&mut self, w: ExprId, x: ExprId) -> Result { + // Destination width expression should have known integer value. + let dst: usize = self + .assignment + .try_int_value(w) + .context("destination width of conv_to expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Expression type should be a bit-vector of known width. + let src = self + .assignment + .try_bit_vector_width(x) + .context("source of conv_to expression should be a bit-vector of known width")?; + + // Handle depending on source and destination widths. + match dst.cmp(&src) { + Ordering::Greater => { + let padding = self.declare_bit_vec("conv_to_padding", dst - src)?; + Ok(self.smt.concat(padding, self.expr_atom(x))) + } + Ordering::Less => { + let high_bit = dst.checked_sub(1).unwrap(); + Ok(self.extract(high_bit, 0, self.expr_atom(x))) + } + Ordering::Equal => Ok(self.expr_atom(x)), + } + } + + fn int_to_bv(&self, w: ExprId, x: ExprId) -> Result { + // Destination width expression should have known integer value. + let width: usize = self + .assignment + .try_int_value(w) + .context("destination width of int2bv expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Build int2bv expression. + Ok(self.int2bv(width, self.expr_atom(x))) + } + + fn width_of(&self, x: ExprId) -> Result { + // Expression type should be a bit-vector of known width. + let width = self + .assignment + .try_bit_vector_width(x) + .context("target of width_of expression should be a bit-vector of known width")?; + + // Substitute known constant width. + Ok(self.smt.numeral(width)) + } + + fn verification_condition(&mut self) -> Result<()> { + // (not ( => )) + let assumptions = self.all(&self.conditions.assumptions); + let assertions = self.all(&self.conditions.assertions); + let vc = self.smt.imp(assumptions, assertions); + self.smt.assert(self.smt.not(vc))?; + Ok(()) + } + + /// Zero-extend an SMT bit vector to a wider bit vector by adding `padding` + /// zeroes to the front. + fn zero_extend(&self, padding: usize, v: SExpr) -> SExpr { + if padding == 0 { + return v; + } + self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("zero_extend"), + self.smt.numeral(padding), + ]), + v, + ]) + } + + /// Sign-extend an SMT bit vector to a wider bit vector. + fn sign_extend(&self, padding: usize, v: SExpr) -> SExpr { + if padding == 0 { + return v; + } + self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("sign_extend"), + self.smt.numeral(padding), + ]), + v, + ]) + } + + fn extract(&self, high_bit: usize, low_bit: usize, v: SExpr) -> SExpr { + assert!(low_bit <= high_bit); + self.smt + .extract(high_bit.try_into().unwrap(), low_bit.try_into().unwrap(), v) + } + + /// Convert an SMT integer to a bit vector of a given width. + fn int2bv(&self, width: usize, value: SExpr) -> SExpr { + self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("int2bv"), + self.smt.numeral(width), + ]), + value, + ]) + } + + /// Floating point special values. + fn fp_value(&mut self, op: &str, w: ExprId) -> Result { + let width = self + .assignment + .try_int_value(w) + .context("floating point constant width should have known integer value")? + .try_into()?; + let (eb, sb) = Self::fp_exponent_significand_bits(width)?; + let result_fp = self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom(op), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point unary operand without rounding. + fn fp_unary(&mut self, op: &str, x: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + + // Unary expression. + let result_fp = self.smt.list(vec![self.smt.atom(op), x]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point test operation without rounding, to boolean. + fn fp_test(&mut self, op: &str, x: ExprId, y: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + let y = self.to_fp(self.expr_atom(y), width)?; + + // Binary result, no conversion needed after test + Ok(self.smt.list(vec![self.smt.atom(op), x, y])) + } + + /// Floating point binary operand without rounding. + fn fp_binary(&mut self, op: &str, x: ExprId, y: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + let y = self.to_fp(self.expr_atom(y), width)?; + + // Binary expression. + let result_fp = self.smt.list(vec![self.smt.atom(op), x, y]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point unary operand with rounding. + fn fp_rounding_unary(&mut self, op: &str, rounding_mode: &str, x: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + + // Unary expression. + let result_fp = self + .smt + .list(vec![self.smt.atom(op), self.smt.atom(rounding_mode), x]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point binary operand with rounding. + fn fp_rounding_binary(&mut self, op: &str, x: ExprId, y: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + let y = self.to_fp(self.expr_atom(y), width)?; + + // Binary expression. + let result_fp = self + .smt + .list(vec![self.smt.atom(op), self.smt.atom(ROUNDING_MODE), x, y]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point square root, modeled as a deterministic uninterpreted + /// function over the input bits rather than the bit-exact `fp.sqrt`. + /// + /// `fp.sqrt` is the one floating-point operation that current SMT solvers + /// (both Z3 and CVC5) cannot decide in a reasonable time in this encoding: + /// the bit-vector/`to_fp` round-trip around `fp.sqrt` makes even the + /// applicability precheck time out. The verification only relies on sqrt + /// being a *deterministic* function of its input -- both the instruction + /// spec and the lowering apply the same sqrt to the same value, and both + /// handle NaN/zero/infinity/negative inputs explicitly before ever + /// reaching `fp.sqrt`, so its bit-exact value is never relied upon. Modeling + /// it as an uninterpreted function is therefore sound for proving + /// lowering/spec equivalence (congruence forces equal inputs to equal + /// outputs, and unequal inputs remain free to differ) while keeping the + /// queries decidable. This mirrors the custom encodings used for other + /// solver-hostile operations (`cls`, `clz`, `popcnt`, `rev`). + fn fp_sqrt(&mut self, x: ExprId) -> Result { + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + // Declare the per-width uninterpreted sqrt function once, then share the + // same symbol across every sqrt occurrence so that equal inputs are + // forced (by congruence) to produce equal outputs. + let func = format!("fp.sqrt_uf_{width}"); + if self.sqrt_uf_widths.insert(width) { + let bv_sort = self.smt.bit_vec_sort(self.smt.numeral(width)); + self.smt.declare_fun(&func, vec![bv_sort], bv_sort)?; + } + + Ok(self.smt.list(vec![self.smt.atom(func), self.expr_atom(x)])) + } + + /// Floating point unary predicate. + fn fp_unary_predicate(&mut self, op: &str, x: ExprId) -> Result { + // Convert operand to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + + // Emit expression. + Ok(self.smt.list(vec![self.smt.atom(op), x])) + } + + /// Represent an expression in SMT-LIB floating-point type. + fn to_fp(&self, x: SExpr, width: usize) -> Result { + let (eb, sb) = Self::fp_exponent_significand_bits(width)?; + Ok(self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("to_fp"), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]), + x, + ])) + } + + fn fp_from_expr(&mut self, w: ExprId, xid: ExprId, signed: bool) -> Result { + // Destination width expression should have known integer value. + let width: usize = self + .assignment + .try_int_value(w) + .context("destination width of to_fp expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + let x = self.expr_atom(xid); + let (eb, sb) = Self::fp_exponent_significand_bits(width)?; + let fp = self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt + .atom(if signed { "to_fp" } else { "to_fp_unsigned" }), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]), + self.smt.atom(ROUNDING_MODE), + x, + ]); + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec("conv", width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, fp))?; + + Ok(result) + } + + fn fp_to_bv(&mut self, w: ExprId, x: ExprId, signed: bool) -> Result { + // Destination width expression should have known integer value. + let width: usize = self + .assignment + .try_int_value(w) + .context("destination width of fp_to_bv expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + let x = self.to_fp(self.expr_atom(x), width)?; + + let fp: SExpr = self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt + .atom(if signed { "fp.to_sbv" } else { "fp.to_ubv" }), + self.smt.numeral(width), + ]), + self.smt.atom(ROUNDING_MODE), + x, + ]); + + Ok(fp) + } + + fn fp_from_fp(&mut self, w: ExprId, xid: ExprId) -> Result { + // Destination width expression should have known integer value. + let new_width: usize = self + .assignment + .try_int_value(w) + .context( + "destination width of to_fp_from_fp expression should have known integer value", + )? + .try_into() + .expect("width should be representable as usize"); + + // Convert operand to floating point. + let width = self + .assignment + .try_bit_vector_width(xid) + .context("floating point expression must be a bit-vector of known width")?; + let x = self.to_fp(self.expr_atom(xid), width)?; + + let (eb, sb) = Self::fp_exponent_significand_bits(new_width)?; + let fp = self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("to_fp"), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]), + self.smt.atom(ROUNDING_MODE), + x, + ]); + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec("conv", new_width)?; + let result_as_fp = self.to_fp(result, new_width)?; + self.smt.assert(self.smt.eq(result_as_fp, fp))?; + + Ok(result) + } + + fn fp_exponent_significand_bits(width: usize) -> Result<(usize, usize)> { + Ok(match width { + 32 => (8, 24), + 64 => (11, 53), + _ => bail!("unsupported floating-point width"), + }) + } + + /// Parse a constant SMT expression. + fn const_from_sexpr(&self, sexpr: SExpr) -> Result { + match self.smt.get(sexpr) { + SExprData::Atom(a) => Self::const_from_literal(a), + SExprData::List(exprs) => self.const_from_qualified_abstract_value(exprs), + SExprData::String(s) => bail!("unsupported smt const: {s}"), + } + } + + /// Parse a constant from an SMT literal. + fn const_from_literal(atom: &str) -> Result { + if atom == "true" { + Ok(Const::Bool(true)) + } else if atom == "false" { + Ok(Const::Bool(false)) + } else if let Some(x) = atom.strip_prefix("#x") { + Ok(Const::BitVector( + x.len() * 4, + BigUint::from_str_radix(x, 16)?, + )) + } else if let Some(x) = atom.strip_prefix("#b") { + Ok(Const::BitVector(x.len(), BigUint::from_str_radix(x, 2)?)) + } else if atom.starts_with(|c: char| c.is_ascii_digit()) { + Ok(Const::Int(atom.parse()?)) + } else { + bail!("unsupported smt literal: {atom}") + } + } + + /// Parse a constant value of a declared sort from an SMT qualified abstract value. + fn const_from_qualified_abstract_value(&self, exprs: &[SExpr]) -> Result { + // This logic is specific to CVC5's representation of declared sort + // abstract values. Z3 uses a different format. This function is + // therefore careful to check for the exact format it expects from CVC5. + + // Expect a list of atoms. + let atoms = exprs + .iter() + .map(|e| match self.smt.get(*e) { + SExprData::Atom(a) => Ok(a), + _ => bail!("expected atom in qualified identifier"), + }) + .collect::>>()?; + + // Expect the list to be of the form (as @ ). + let ["as", value, sort] = atoms.as_slice() else { + bail!("unsupported qualified identifier: {atoms:?}"); + }; + + // Expect an abstract value. + if !value.starts_with('@') { + bail!("expected qualified identifier constant to have abstract value"); + } + + // Construct constant based on the sort. + if sort == &UNSPECIFIED_SORT { + Ok(Const::Unspecified) + } else if sort == &UNIT_SORT { + todo!("unit sort") + } else { + bail!("unknown sort: '{sort}'"); + } + } + + fn all(&self, xs: &[ExprId]) -> SExpr { + self.smt.and_many(xs.iter().map(|x| self.expr_atom(*x))) + } + + fn expr_atom(&self, x: ExprId) -> SExpr { + self.smt.atom(self.expr_name(x)) + } + + fn expr_name(&self, x: ExprId) -> String { + let expr = &self.conditions.exprs[x.index()]; + if let Expr::Variable(v) = expr { + format!( + "{}_{}", + self.conditions.variables[v.index()].name, + x.index() + ) + } else { + format!("e{}", x.index()) + } + } + + fn declare_bit_vec(&mut self, name: &str, n: usize) -> Result { + let name = format!("${name}{}", self.tmp_idx); + self.tmp_idx += 1; + let sort = self.smt.bit_vec_sort(self.smt.numeral(n)); + self.smt.declare_const(&name, sort)?; + Ok(self.smt.atom(name)) + } + + fn encode_rotate( + &self, + op: RotationDirection, + source: SExpr, + amount: SExpr, + width: usize, + ) -> SExpr { + // SMT bitvector rotate_left requires that the rotate amount be + // statically specified. Instead, to use a dynamic amount, desugar + // to shifts and bit arithmetic. + let width_as_bv = self.smt.binary(width, width); + let wrapped_amount = self.smt.bvurem(amount, width_as_bv); + let wrapped_delta = self.smt.bvsub(width_as_bv, wrapped_amount); + match op { + RotationDirection::Left => self.smt.bvor( + self.smt.bvshl(source, wrapped_amount), + self.smt.bvlshr(source, wrapped_delta), + ), + RotationDirection::Right => self.smt.bvor( + self.smt.bvshl(source, wrapped_delta), + self.smt.bvlshr(source, wrapped_amount), + ), + } + } + + fn error(&self, x: ExprId, msg: impl Into) -> Error { + self.conditions.error_at_expr(self.prog, x, msg) + } +} diff --git a/cranelift/isle/veri/veri/src/spec.rs b/cranelift/isle/veri/veri/src/spec.rs new file mode 100644 index 000000000000..6d77243d556d --- /dev/null +++ b/cranelift/isle/veri/veri/src/spec.rs @@ -0,0 +1,999 @@ +use anyhow::{Ok, Result, bail, format_err}; +use cranelift_isle::{ + ast::{self, AttrKind, AttrTarget, Def, Ident, Model, ModelType, Modifies, SpecOp}, + lexer::Pos, + sema::{ReturnKind, RuleId, Sym, Term, TermEnv, TermId, TypeEnv, TypeId}, +}; +use std::{ + collections::{HashMap, HashSet, hash_map::Entry}, + fmt::Debug, +}; + +use crate::types::{Compound, Const}; + +/// Positioned attaches positional information to a wrapped object. +#[derive(Clone)] +pub struct Positioned { + pub pos: Pos, + pub x: X, +} + +impl Positioned { + fn new(pos: Pos, x: X) -> Box { + Box::new(Self { pos, x }) + } +} + +impl Debug for Positioned { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Positioned") + .field(&self.pos) + .field(&self.x) + .finish() + } +} + +pub type Expr = Box>; + +/// Spec expression. +#[derive(Debug, Clone)] +pub enum ExprKind { + // TODO(mbm): plumb positional information through spec expressions + + // Terminal nodes + Var(Ident), + Const(Const), + As(Expr, Compound), + Constructor(Constructor), + Field(Ident, Expr), + Discriminator(Ident, Expr), + + // Get the width of a bitvector + WidthOf(Expr), + + // Boolean operations + Not(Expr), + And(Vec), + Or(Vec), + Imp(Expr, Expr), + Eq(Expr, Expr), + Lt(Expr, Expr), + Lte(Expr, Expr), + Gt(Expr, Expr), + Gte(Expr, Expr), + + BVSgt(Expr, Expr), + BVSge(Expr, Expr), + BVSlt(Expr, Expr), + BVSle(Expr, Expr), + BVUgt(Expr, Expr), + BVUge(Expr, Expr), + BVUlt(Expr, Expr), + BVUle(Expr, Expr), + + BVSaddo(Expr, Expr), + + // Integer arithmetic + Add(Expr, Expr), + Sub(Expr, Expr), + Mul(Expr, Expr), + + // Bitvector operations + + // Unary operators + BVNeg(Expr), + BVNot(Expr), + Cls(Expr), + Clz(Expr), + Rev(Expr), + Popcnt(Expr), + + // Binary operators + BVAdd(Expr, Expr), + BVSub(Expr, Expr), + BVMul(Expr, Expr), + BVUDiv(Expr, Expr), + BVSDiv(Expr, Expr), + BVURem(Expr, Expr), + BVSRem(Expr, Expr), + BVAnd(Expr, Expr), + BVOr(Expr, Expr), + BVXor(Expr, Expr), + BVRotl(Expr, Expr), + BVRotr(Expr, Expr), + BVShl(Expr, Expr), + BVLShr(Expr, Expr), + BVAShr(Expr, Expr), + + // Conversions + BVZeroExt(Expr, Expr), + BVSignExt(Expr, Expr), + // Conversion to wider/narrower bits, without an explicit extend. + BVConvTo(Expr, Expr), + ToFP(Expr, Expr), + ToFPUnsigned(Expr, Expr), + ToFPFromFP(Expr, Expr), + FPToUBV(Expr, Expr), + FPToSBV(Expr, Expr), + + // Extract specified bits + BVExtract(usize, usize, Expr), + + // Concatenate bitvectors. + BVConcat(Vec), + BVReplicate(Expr, usize), + + // Convert between integers and bitvector. + Int2BV(Expr, Expr), + BV2Nat(Expr), + + // Floating point. + FPPositiveInfinity(Expr), + FPNegativeInfinity(Expr), + FPPositiveZero(Expr), + FPNegativeZero(Expr), + FPNaN(Expr), + FPEq(Expr, Expr), + FPNe(Expr, Expr), + FPLt(Expr, Expr), + FPGt(Expr, Expr), + FPLe(Expr, Expr), + FPGe(Expr, Expr), + FPAdd(Expr, Expr), + FPSub(Expr, Expr), + FPMul(Expr, Expr), + FPDiv(Expr, Expr), + FPMin(Expr, Expr), + FPMax(Expr, Expr), + FPNeg(Expr), + FPCeil(Expr), + FPFloor(Expr), + FPSqrt(Expr), + FPTrunc(Expr), + FPNearest(Expr), + FPIsZero(Expr), + FPIsInfinite(Expr), + FPIsNaN(Expr), + FPIsNegative(Expr), + FPIsPositive(Expr), + + // Conditional if-then-else + Conditional(Expr, Expr, Expr), + // Switch + Switch(Expr, Vec<(Expr, Expr)>), + // Match + Match(Expr, Vec), + // Let bindings + Let(Vec<(Ident, Expr)>, Expr), + // With scope. + With(Vec, Expr), + // Macro definition. + Macro(Vec, Expr), + // Macro expansion. + Expand(Ident, Vec), +} + +macro_rules! unary_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert_eq!( + $args.len(), + 1, + "Unexpected number of args for unary operator at {:?}", + $pos + ); + $expr(expr_from_ast(&$args[0])) + }}; +} + +macro_rules! binary_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert_eq!( + $args.len(), + 2, + "Unexpected number of args for binary operator at {:?}", + $pos + ); + $expr(expr_from_ast(&$args[0]), expr_from_ast(&$args[1])) + }}; +} + +macro_rules! ternary_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert_eq!( + $args.len(), + 3, + "Unexpected number of args for ternary operator at {:?}", + $pos + ); + $expr( + expr_from_ast(&$args[0]), + expr_from_ast(&$args[1]), + expr_from_ast(&$args[2]), + ) + }}; +} + +macro_rules! variadic_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert!( + $args.len() >= 1, + "Unexpected number of args for variadic binary operator {:?}", + $pos + ); + $expr(exprs_from_ast($args)) + }}; +} + +fn expr_from_ast(expr: &ast::SpecExpr) -> Expr { + Positioned::new(expr.pos(), ExprKind::from_ast(expr)) +} + +fn exprs_from_ast(exprs: &[ast::SpecExpr]) -> Vec { + exprs.iter().map(expr_from_ast).collect() +} + +fn var_from_ident(ident: Ident) -> Expr { + Positioned::new(ident.1, ExprKind::Var(ident)) +} + +impl ExprKind { + fn from_ast(expr: &ast::SpecExpr) -> ExprKind { + match expr { + ast::SpecExpr::ConstInt { val, pos: _ } => ExprKind::Const(Const::Int(*val)), + ast::SpecExpr::ConstBool { val, pos: _ } => ExprKind::Const(Const::Bool(*val)), + ast::SpecExpr::ConstBitVec { val, width, pos: _ } => { + ExprKind::Const(Const::BitVector(*width, (*val).into())) + } + ast::SpecExpr::Var { var, pos: _ } => ExprKind::Var(var.clone()), + ast::SpecExpr::As { x, ty, pos: _ } => { + ExprKind::As(expr_from_ast(x), Compound::from_ast(ty)) + } + ast::SpecExpr::Field { field, x, pos: _ } => { + ExprKind::Field(field.clone(), expr_from_ast(x)) + } + ast::SpecExpr::Discriminator { variant, x, pos: _ } => { + ExprKind::Discriminator(variant.clone(), expr_from_ast(x)) + } + ast::SpecExpr::Op { op, args, pos } => match op { + // Unary + SpecOp::Not => unary_expr!(ExprKind::Not, args, pos), + SpecOp::BVNot => unary_expr!(ExprKind::BVNot, args, pos), + SpecOp::BVNeg => unary_expr!(ExprKind::BVNeg, args, pos), + SpecOp::Cls => unary_expr!(ExprKind::Cls, args, pos), + SpecOp::Rev => unary_expr!(ExprKind::Rev, args, pos), + SpecOp::Clz => unary_expr!(ExprKind::Clz, args, pos), + SpecOp::Popcnt => unary_expr!(ExprKind::Popcnt, args, pos), + + // Variadic binops + SpecOp::And => variadic_expr!(ExprKind::And, args, pos), + SpecOp::Or => variadic_expr!(ExprKind::Or, args, pos), + + // Binary + SpecOp::Eq => binary_expr!(ExprKind::Eq, args, pos), + SpecOp::Lt => binary_expr!(ExprKind::Lt, args, pos), + SpecOp::Lte => binary_expr!(ExprKind::Lte, args, pos), + SpecOp::Gt => binary_expr!(ExprKind::Gt, args, pos), + SpecOp::Gte => binary_expr!(ExprKind::Gte, args, pos), + SpecOp::Imp => binary_expr!(ExprKind::Imp, args, pos), + SpecOp::Add => binary_expr!(ExprKind::Add, args, pos), + SpecOp::Sub => binary_expr!(ExprKind::Sub, args, pos), + SpecOp::Mul => binary_expr!(ExprKind::Mul, args, pos), + SpecOp::BVAnd => binary_expr!(ExprKind::BVAnd, args, pos), + SpecOp::BVOr => binary_expr!(ExprKind::BVOr, args, pos), + SpecOp::BVXor => binary_expr!(ExprKind::BVXor, args, pos), + SpecOp::BVAdd => binary_expr!(ExprKind::BVAdd, args, pos), + SpecOp::BVSub => binary_expr!(ExprKind::BVSub, args, pos), + SpecOp::BVMul => binary_expr!(ExprKind::BVMul, args, pos), + SpecOp::BVSdiv => binary_expr!(ExprKind::BVSDiv, args, pos), + SpecOp::BVUdiv => binary_expr!(ExprKind::BVUDiv, args, pos), + SpecOp::BVUrem => binary_expr!(ExprKind::BVURem, args, pos), + SpecOp::BVSrem => binary_expr!(ExprKind::BVSRem, args, pos), + SpecOp::BVShl => binary_expr!(ExprKind::BVShl, args, pos), + SpecOp::BVLshr => binary_expr!(ExprKind::BVLShr, args, pos), + SpecOp::BVAshr => binary_expr!(ExprKind::BVAShr, args, pos), + SpecOp::BVUle => binary_expr!(ExprKind::BVUle, args, pos), + SpecOp::BVUlt => binary_expr!(ExprKind::BVUlt, args, pos), + SpecOp::BVUgt => binary_expr!(ExprKind::BVUgt, args, pos), + SpecOp::BVUge => binary_expr!(ExprKind::BVUge, args, pos), + SpecOp::BVSlt => binary_expr!(ExprKind::BVSlt, args, pos), + SpecOp::BVSle => binary_expr!(ExprKind::BVSle, args, pos), + SpecOp::BVSgt => binary_expr!(ExprKind::BVSgt, args, pos), + SpecOp::BVSge => binary_expr!(ExprKind::BVSge, args, pos), + SpecOp::BVSaddo => binary_expr!(ExprKind::BVSaddo, args, pos), + SpecOp::Rotr => binary_expr!(ExprKind::BVRotr, args, pos), + SpecOp::Rotl => binary_expr!(ExprKind::BVRotl, args, pos), + + // Conversions + SpecOp::ZeroExt => binary_expr!(ExprKind::BVZeroExt, args, pos), + SpecOp::SignExt => binary_expr!(ExprKind::BVSignExt, args, pos), + SpecOp::ConvTo => binary_expr!(ExprKind::BVConvTo, args, pos), + SpecOp::Concat => variadic_expr!(ExprKind::BVConcat, args, pos), + SpecOp::Replicate => { + // TODO(mbm): return error instead of assert + assert_eq!( + args.len(), + 2, + "Unexpected number of args for extract operator at {pos:?}", + ); + let repeat = spec_expr_to_usize(&args[1]).unwrap(); + assert!( + repeat > 0, + "Unexpected repeat count for replicate operator at {pos:?}", + ); + ExprKind::BVReplicate(expr_from_ast(&args[0]), repeat) + } + SpecOp::Extract => { + // TODO(mbm): return error instead of assert + assert_eq!( + args.len(), + 3, + "Unexpected number of args for extract operator at {pos:?}", + ); + ExprKind::BVExtract( + spec_expr_to_usize(&args[0]).unwrap(), + spec_expr_to_usize(&args[1]).unwrap(), + expr_from_ast(&args[2]), + ) + } + SpecOp::Int2BV => binary_expr!(ExprKind::Int2BV, args, pos), + SpecOp::BV2Nat => unary_expr!(ExprKind::BV2Nat, args, pos), + SpecOp::WidthOf => unary_expr!(ExprKind::WidthOf, args, pos), + SpecOp::ToFP => binary_expr!(ExprKind::ToFP, args, pos), + SpecOp::ToFPUnsigned => binary_expr!(ExprKind::ToFPUnsigned, args, pos), + SpecOp::ToFPFromFP => binary_expr!(ExprKind::ToFPFromFP, args, pos), + SpecOp::FPToUBV => binary_expr!(ExprKind::FPToUBV, args, pos), + SpecOp::FPToSBV => binary_expr!(ExprKind::FPToSBV, args, pos), + + // Floating point (IEEE) + SpecOp::FPPositiveInfinity => unary_expr!(ExprKind::FPPositiveInfinity, args, pos), + SpecOp::FPNegativeInfinity => unary_expr!(ExprKind::FPNegativeInfinity, args, pos), + SpecOp::FPPositiveZero => unary_expr!(ExprKind::FPPositiveZero, args, pos), + SpecOp::FPNegativeZero => unary_expr!(ExprKind::FPNegativeZero, args, pos), + SpecOp::FPNaN => unary_expr!(ExprKind::FPNaN, args, pos), + SpecOp::FPEq => binary_expr!(ExprKind::FPEq, args, pos), + SpecOp::FPNe => binary_expr!(ExprKind::FPNe, args, pos), + SpecOp::FPLt => binary_expr!(ExprKind::FPLt, args, pos), + SpecOp::FPGt => binary_expr!(ExprKind::FPGt, args, pos), + SpecOp::FPLe => binary_expr!(ExprKind::FPLe, args, pos), + SpecOp::FPGe => binary_expr!(ExprKind::FPGe, args, pos), + SpecOp::FPAdd => binary_expr!(ExprKind::FPAdd, args, pos), + SpecOp::FPSub => binary_expr!(ExprKind::FPSub, args, pos), + SpecOp::FPMul => binary_expr!(ExprKind::FPMul, args, pos), + SpecOp::FPDiv => binary_expr!(ExprKind::FPDiv, args, pos), + SpecOp::FPMin => binary_expr!(ExprKind::FPMin, args, pos), + SpecOp::FPMax => binary_expr!(ExprKind::FPMax, args, pos), + SpecOp::FPNeg => unary_expr!(ExprKind::FPNeg, args, pos), + SpecOp::FPCeil => unary_expr!(ExprKind::FPCeil, args, pos), + SpecOp::FPFloor => unary_expr!(ExprKind::FPFloor, args, pos), + SpecOp::FPSqrt => unary_expr!(ExprKind::FPSqrt, args, pos), + SpecOp::FPTrunc => unary_expr!(ExprKind::FPTrunc, args, pos), + SpecOp::FPNearest => unary_expr!(ExprKind::FPNearest, args, pos), + SpecOp::FPIsZero => unary_expr!(ExprKind::FPIsZero, args, pos), + SpecOp::FPIsInfinite => unary_expr!(ExprKind::FPIsInfinite, args, pos), + SpecOp::FPIsNaN => unary_expr!(ExprKind::FPIsNaN, args, pos), + SpecOp::FPIsNegative => unary_expr!(ExprKind::FPIsNegative, args, pos), + SpecOp::FPIsPositive => unary_expr!(ExprKind::FPIsPositive, args, pos), + + // Conditionals + SpecOp::If => ternary_expr!(ExprKind::Conditional, args, pos), + SpecOp::Switch => { + assert!( + args.len() > 1, + "Unexpected number of args for switch operator {pos:?}", + ); + let on = expr_from_ast(&args[0]); + let arms: Vec<_> = args[1..] + .iter() + .map(|p| match p { + ast::SpecExpr::Pair { l, r, pos: _ } => { + (expr_from_ast(l), expr_from_ast(r)) + } + // TODO(mbm): error rather than panic for non-pair in switch, since it's not actually unreachable + _ => unreachable!("switch expression arguments must be pairs"), + }) + .collect(); + ExprKind::Switch(on, arms) + } + }, + ast::SpecExpr::Match { x, arms, pos: _ } => { + let x = expr_from_ast(x); + let arms = arms + .iter() + .map(|arm| Arm { + variant: arm.variant.clone(), + args: arm.args.clone(), + body: expr_from_ast(&arm.body), + }) + .collect(); + ExprKind::Match(x, arms) + } + ast::SpecExpr::Let { defs, body, pos: _ } => { + let defs = defs + .iter() + .map(|(ident, x)| (ident.clone(), expr_from_ast(x))) + .collect(); + let body = expr_from_ast(body); + ExprKind::Let(defs, body) + } + ast::SpecExpr::With { + decls, + body, + pos: _, + } => { + let decls = decls.clone(); + let body = expr_from_ast(body); + ExprKind::With(decls, body) + } + ast::SpecExpr::Pair { l, r, pos: _ } => { + unreachable!( + "pairs must only occur in switch expressions, {:?} {:?}", + l, r + ) + } + ast::SpecExpr::Enum { + name, + variant, + args, + pos: _, + } => ExprKind::Constructor(Constructor::Enum { + name: name.clone(), + variant: variant.clone(), + args: exprs_from_ast(args), + }), + ast::SpecExpr::Struct { fields, pos: _ } => { + ExprKind::Constructor(Constructor::Struct { + fields: fields.iter().map(FieldInit::from_ast).collect(), + }) + } + ast::SpecExpr::Macro { + params, + body, + pos: _, + } => ExprKind::Macro(params.clone(), expr_from_ast(body)), + ast::SpecExpr::Expand { name, args, pos: _ } => { + ExprKind::Expand(name.clone(), exprs_from_ast(args)) + } + } + } +} + +fn spec_expr_to_usize(expr: &ast::SpecExpr) -> Option { + match expr { + &ast::SpecExpr::ConstInt { val, pos: _ } => { + // TODO(mbm): return error rather than unwrap + Some(val.try_into().expect("constant should be unsigned size")) + } + _ => None, + } +} + +#[derive(Debug, Clone)] +pub enum Constructor { + Enum { + // TODO(mbm): Enum identifiers should be mapped to TermId? + name: Ident, + variant: Ident, + args: Vec, + }, + Struct { + fields: Vec, + }, +} + +#[derive(Debug, Clone)] +pub struct Arm { + pub variant: Ident, + pub args: Vec, + pub body: Expr, +} + +#[derive(Debug, Clone)] +pub struct FieldInit { + pub name: Ident, + pub value: Expr, +} + +impl FieldInit { + fn from_ast(field: &ast::FieldInit) -> Self { + Self { + name: field.name.clone(), + value: expr_from_ast(&field.value), + } + } +} + +static RESULT: &str = "result"; + +pub struct Spec { + pub args: Vec, + pub ret: Ident, + pub provides: Vec, + pub requires: Vec, + pub matches: Vec, + pub modifies: Vec, + pub pos: Pos, +} + +impl Spec { + fn new() -> Self { + Self { + args: Vec::new(), + ret: Self::result_ident(), + provides: Vec::new(), + requires: Vec::new(), + matches: Vec::new(), + modifies: Vec::new(), + pos: Pos::default(), + } + } + + fn from_ast(spec: &ast::Spec) -> Self { + Self { + args: spec.args.clone(), + ret: Self::result_ident(), + provides: exprs_from_ast(&spec.provides), + requires: exprs_from_ast(&spec.requires), + matches: exprs_from_ast(&spec.matches), + modifies: spec.modifies.clone(), + pos: spec.pos, + } + } + + fn result_ident() -> Ident { + Ident(RESULT.to_string(), Pos::default()) + } +} + +#[derive(Debug, Clone)] +pub struct State { + pub name: Ident, + pub ty: Compound, + pub default: Expr, +} + +#[derive(Debug, Clone)] +pub struct Signature { + pub args: Vec, + pub ret: Compound, +} + +impl Signature { + fn from_ast(sig: &ast::Signature) -> Self { + Self { + args: sig.args.iter().map(Compound::from_ast).collect(), + ret: Compound::from_ast(&sig.ret), + } + } +} + +impl std::fmt::Display for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "({args}) -> {ret}", + args = self + .args + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "), + ret = self.ret + ) + } +} + +pub struct Macro { + pub name: Ident, + pub params: Vec, + pub body: Expr, +} + +pub struct SpecEnv { + /// Specification for the given term. + pub term_spec: HashMap, + + /// State elements. + pub state: Vec, + + /// Terms that should be chained. + pub chain: HashSet, + + /// Tags applied to each term. + pub term_tags: HashMap>, + + // Type instantiations for the given term. + pub term_instantiations: HashMap>, + + /// Rules for which priority is significant. + pub priority: HashSet, + + /// Tags applied to each rule. + pub rule_tags: HashMap>, + + /// Model for the given type. + pub type_model: HashMap, + + /// Value for the given constant. + pub const_value: HashMap, + + /// Macro definitions. + pub macros: HashMap, +} + +impl SpecEnv { + pub fn from_ast(defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) -> Result { + let mut env = Self { + term_spec: HashMap::new(), + state: Vec::new(), + chain: HashSet::new(), + term_tags: HashMap::new(), + term_instantiations: HashMap::new(), + priority: HashSet::new(), + rule_tags: HashMap::new(), + type_model: HashMap::new(), + const_value: HashMap::new(), + macros: HashMap::new(), + }; + + env.collect_models(defs, tyenv); + env.derive_type_models(tyenv)?; + env.derive_enum_variant_specs(termenv, tyenv)?; + env.collect_state(defs)?; + env.collect_instantiations(defs, termenv, tyenv); + env.collect_specs(defs, termenv, tyenv)?; + env.collect_attrs(defs, termenv, tyenv)?; + env.collect_macros(defs); + env.check_option_return_term_specs_uses_matches(termenv, tyenv)?; + env.check_for_chained_terms_with_spec(); + + Ok(env) + } + + fn collect_models(&mut self, defs: &[Def], tyenv: &TypeEnv) { + for def in defs { + if let ast::Def::Model(Model { name, val }) = def { + match val { + ast::ModelValue::TypeValue(model_type) => { + self.set_model_type(name, model_type, tyenv); + } + ast::ModelValue::ConstValue(val) => { + // TODO(mbm): error on missing constant name rather than panic + let sym = tyenv.intern(name).expect("constant name should be defined"); + // TODO(mbm): enforce that the expression is constant. + // TODO(mbm): ensure the type of the expression matches the type of the + self.const_value.insert(sym, expr_from_ast(val)); + } + } + } + } + } + + fn derive_type_models(&mut self, tyenv: &TypeEnv) -> Result<()> { + for ty in &tyenv.types { + // Has an explicit model already been specified? + if self.has_model(ty.id()) { + continue; + } + + // Derive a model from ISLE type, if possible. + let Some(derived_type) = Compound::from_isle(ty, tyenv) else { + continue; + }; + + // Register derived. + self.type_model.insert(ty.id(), derived_type); + } + Ok(()) + } + + fn derive_enum_variant_specs(&mut self, termenv: &TermEnv, tyenv: &TypeEnv) -> Result<()> { + for model in self.type_model.values() { + if let Compound::Enum(e) = model { + for variant in &e.variants { + // Lookup the corresponding term. + let full_name = ast::Variant::full_name(&e.name, &variant.name); + let term_id = + termenv + .get_term_by_name(tyenv, &full_name) + .ok_or(format_err!( + "could not find variant term {name}", + name = full_name.0 + ))?; + + // Synthesize spec. + let pos = variant.name.1; + let args: Vec = variant.fields.iter().map(|f| f.name.clone()).collect(); + let constructor = Positioned::new( + pos, + ExprKind::Constructor(Constructor::Enum { + name: e.name.clone(), + variant: variant.name.clone(), + args: args.iter().cloned().map(var_from_ident).collect(), + }), + ); + + let mut spec = Spec::new(); + spec.args = args; + let ret = var_from_ident(spec.ret.clone()); + spec.provides + .push(Positioned::new(pos, ExprKind::Eq(ret, constructor))); + self.term_spec.insert(term_id, spec); + self.term_tags + .entry(term_id) + .or_default() + .insert("internal_derived_spec".to_string()); + } + } + } + Ok(()) + } + + fn set_model_type(&mut self, name: &Ident, model_type: &ModelType, tyenv: &TypeEnv) { + // TODO(mbm): error on missing type rather than panic + let type_id = tyenv + .get_type_by_name(name) + .expect("type name should be defined"); + // TODO(mbm): error on duplicate model + assert!( + !self.type_model.contains_key(&type_id), + "duplicate type model: {name}", + name = name.0 + ); + self.type_model + .insert(type_id, Compound::from_ast(model_type)); + } + + fn collect_state(&mut self, defs: &[Def]) -> Result<()> { + // Collect states. + for def in defs { + if let ast::Def::State(ast::State { + name, + ty, + default, + pos: _, + }) = def + { + let ty = Compound::from_ast(ty); + let default = expr_from_ast(default); + self.state.push(State { + name: name.clone(), + ty, + default, + }); + } + } + + // Check for duplicates. + let mut names = HashSet::new(); + for state in &self.state { + let name = &state.name.0; + if names.contains(name) { + bail!("duplicate state {name}"); + } + names.insert(name); + } + + Ok(()) + } + + fn collect_instantiations(&mut self, defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) { + // Collect form signatures first, as they may be referenced by instantiations. + let mut form_signature = HashMap::new(); + for def in defs { + if let ast::Def::Form(form) = def { + let signatures: Vec<_> = form.signatures.iter().map(Signature::from_ast).collect(); + form_signature.insert(form.name.0.clone(), signatures); + } + } + + // Collect instantiations. + for def in defs { + if let ast::Def::Instantiation(inst) = def { + let term_id = termenv.get_term_by_name(tyenv, &inst.term).unwrap(); + let sigs = match &inst.form { + Some(form) => form_signature[&form.0].clone(), + None => inst.signatures.iter().map(Signature::from_ast).collect(), + }; + self.term_instantiations.insert(term_id, sigs); + } + } + } + + fn collect_specs(&mut self, defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) -> Result<()> { + for def in defs { + if let ast::Def::Spec(spec) = def { + let term_id = termenv + .get_term_by_name(tyenv, &spec.term) + .ok_or(format_err!( + "spec for unknown term {name}", + name = spec.term.0 + ))?; + match self.term_spec.entry(term_id) { + Entry::Occupied(_) => { + bail!("duplicate spec for term {name}", name = spec.term.0) + } + Entry::Vacant(e) => { + e.insert(Spec::from_ast(spec)); + } + } + } + } + Ok(()) + } + + fn collect_attrs(&mut self, defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) -> Result<()> { + for def in defs { + if let ast::Def::Attr(attr) = def { + match &attr.target { + AttrTarget::Term(name) => { + let term_id = termenv.get_term_by_name(tyenv, name).ok_or(format_err!( + "attr term '{name}' should exist", + name = name.0 + ))?; + for kind in &attr.kinds { + match kind { + AttrKind::Chain => { + self.chain.insert(term_id); + } + AttrKind::Tag(tag) => { + self.term_tags + .entry(term_id) + .or_default() + .insert(tag.0.clone()); + } + AttrKind::Priority => { + bail!("priority attribute cannot be applied to terms"); + } + } + } + } + AttrTarget::Rule(name) => { + let rule_id = termenv + .get_rule_by_name(tyenv, name) + .ok_or(format_err!("attr rule '{}' does not exist", name.0))?; + for kind in &attr.kinds { + match kind { + AttrKind::Priority => { + self.priority.insert(rule_id); + } + AttrKind::Tag(tag) => { + self.rule_tags + .entry(rule_id) + .or_default() + .insert(tag.0.clone()); + } + AttrKind::Chain => { + bail!("chain attribute cannot be applied to rule"); + } + } + } + } + } + } + } + Ok(()) + } + + fn collect_macros(&mut self, defs: &[Def]) { + for def in defs { + if let ast::Def::SpecMacro(spec_macro) = def { + let body = expr_from_ast(&spec_macro.body); + self.macros.insert( + spec_macro.name.0.clone(), + Macro { + name: spec_macro.name.clone(), + params: spec_macro.params.clone(), + body, + }, + ); + } + } + } + + fn check_option_return_term_specs_uses_matches( + &self, + termenv: &TermEnv, + tyenv: &TypeEnv, + ) -> Result<()> { + for (term_id, spec) in &self.term_spec { + let term = &termenv.terms[term_id.index()]; + if !Self::term_returns_option(term, tyenv) { + continue; + } + if !spec.requires.is_empty() { + bail!( + "term '{name}' requires should be match", + name = tyenv.syms[term.name.index()], + ); + } + } + Ok(()) + } + + fn term_returns_option(term: &Term, tyenv: &TypeEnv) -> bool { + // Constructor + if term.has_constructor() { + return term.is_partial(); + } + + // External extractor + if let Some(sig) = term.extractor_sig(tyenv) { + return sig.ret_kind == ReturnKind::Option; + } + + // Extractor + if term.has_extractor() { + return true; + } + + false + } + + fn check_for_chained_terms_with_spec(&self) { + for term_id in &self.chain { + // TODO(mbm): error rather than panic + assert!( + !self.term_spec.contains_key(term_id), + "chained term should not have spec" + ); + } + } + + /// Resolve any named types in the given compound type. + pub fn resolve_type(&self, ty: &Compound, tyenv: &TypeEnv) -> Result { + ty.resolve(&mut |name| { + let type_id = tyenv + .get_type_by_name(name) + .ok_or(format_err!("unknown type {}", name.0))?; + let ty = self.type_model.get(&type_id).ok_or(format_err!( + "unspecified model for type `{}`: a spec references this type (directly, or \ + via a term signature or another model), but it has no `(model ...)` \ + declaration. Add a `(model {} ...)` form in a spec file describing its \ + representation.", + name.0, + name.0 + ))?; + Ok(ty.clone()) + }) + } + + /// Resolve any named types in the given term signature. + pub fn resolve_signature(&self, sig: &Signature, tyenv: &TypeEnv) -> Result { + Ok(Signature { + args: sig + .args + .iter() + .map(|arg| self.resolve_type(arg, tyenv)) + .collect::>()?, + ret: self.resolve_type(&sig.ret, tyenv)?, + }) + } + + /// Lookup instantiations for the given term, with any named types resolved. + pub fn resolve_term_instantiations( + &self, + term_id: &TermId, + tyenv: &TypeEnv, + ) -> Result> { + let Some(sigs) = self.term_instantiations.get(term_id) else { + return Ok(Vec::new()); + }; + + sigs.iter() + .map(|sig| self.resolve_signature(sig, tyenv)) + .collect::>() + } + + /// Report whether the given term has a specification. + pub fn has_spec(&self, term_id: TermId) -> bool { + self.term_spec.contains_key(&term_id) + } + + pub fn has_model(&self, type_id: TypeId) -> bool { + self.type_model.contains_key(&type_id) + } +} diff --git a/cranelift/isle/veri/veri/src/testing.rs b/cranelift/isle/veri/veri/src/testing.rs new file mode 100644 index 000000000000..7210d7de5495 --- /dev/null +++ b/cranelift/isle/veri/veri/src/testing.rs @@ -0,0 +1,47 @@ +use std::{cmp::Ordering, fmt::Debug}; + +pub fn assert_strictly_increasing(elements: &[T]) +where + T: PartialOrd + Debug, +{ + elements.windows(2).for_each(|p| assert!(p[0] < p[1])); +} + +pub fn assert_partial_order_properties(elements: &[T]) +where + T: PartialOrd + Debug, +{ + // Equality + for a in elements { + for b in elements { + assert_eq!( + a == b, + a.partial_cmp(b) == Some(Ordering::Equal), + "equality property failed: a={a:?} b={b:?}" + ); + } + } + + // Transitivity + for a in elements { + for b in elements { + for c in elements { + assert!( + !(a < b && b < c) || a < c, + "transitivity property failed: a={a:?} b={b:?} c={c:?}" + ); + } + } + } + + // Duality + for a in elements { + for b in elements { + assert_eq!( + a.partial_cmp(b) == Some(Ordering::Less), + b.partial_cmp(a) == Some(Ordering::Greater), + "duality property failed: a={a:?} b={b:?}" + ); + } + } +} diff --git a/cranelift/isle/veri/veri/src/trie.rs b/cranelift/isle/veri/veri/src/trie.rs new file mode 100644 index 000000000000..0f9136b465d4 --- /dev/null +++ b/cranelift/isle/veri/veri/src/trie.rs @@ -0,0 +1,174 @@ +use std::sync::Arc; + +use crate::program::Program; +use crate::types::field_type_by_index; +use cranelift_isle::{ + error::{Errors, ErrorsBuilder}, + files::Files, + sema::{ExternalSig, ReturnKind, TermEnv, TermId, Type, TypeEnv, TypeId}, + trie_again::{self, Binding, BindingId, RuleSet}, +}; + +pub fn build_trie(termenv: &TermEnv, files: Arc) -> Result, Errors> { + let (terms, errors) = trie_again::build(termenv); + if errors.is_empty() { + Ok(terms) + } else { + Err(ErrorsBuilder::new() + .errors(errors) + .files(files.clone()) + .build()) + } +} + +#[derive(Clone, Debug)] +pub enum BindingType { + Base(TypeId), + Option(Box), + Tuple(Vec), +} + +impl BindingType { + pub fn display(&self, tyenv: &TypeEnv) -> String { + match self { + BindingType::Base(type_id) => { + let ty = &tyenv.types[type_id.index()]; + ty.name(tyenv).to_string() + } + BindingType::Option(inner) => format!("Option({})", inner.display(tyenv)), + BindingType::Tuple(inners) => format!( + "({inners})", + inners = inners + .iter() + .map(|inner| inner.display(tyenv)) + .collect::>() + .join(", ") + ), + } + } +} + +/// Determine the type of a given binding. +pub fn binding_type( + binding: &Binding, + term_id: TermId, + prog: &Program, + // TODO(mbm): is there a less ugly way to do binding lookup here? + lookup_binding: impl Fn(BindingId) -> Binding, +) -> BindingType { + match binding { + Binding::ConstInt { ty, .. } + | Binding::ConstBool { ty, .. } + | Binding::MakeVariant { ty, .. } + | Binding::MakeStruct { ty, .. } => BindingType::Base(*ty), + + Binding::ConstPrim { val } => BindingType::Base(prog.tyenv.const_types[val]), + + Binding::Argument { index } => { + let term = &prog.termenv.terms[term_id.index()]; + BindingType::Base(term.arg_tys[index.index()]) + } + + Binding::Extractor { term, .. } => { + // Determine the extractor signature. + let term = &prog.termenv.terms[term.index()]; + let sig = term + .extractor_sig(&prog.tyenv) + .expect("term should have extractor signature"); + external_sig_return_type(&sig) + } + + Binding::Constructor { term, .. } => { + // Determine the constructor signature. + let term = &prog.termenv.terms[term.index()]; + let sig = term + .constructor_sig(&prog.tyenv) + .expect("term should have constructor signature"); + external_sig_return_type(&sig) + } + + Binding::MakeSome { inner } => { + let inner_binding = lookup_binding(*inner); + let inner_ty = binding_type(&inner_binding, term_id, prog, lookup_binding); + BindingType::Option(Box::new(inner_ty)) + } + + Binding::MatchSome { source } => { + let source_binding = lookup_binding(*source); + let source_ty = binding_type(&source_binding, term_id, prog, lookup_binding); + match source_ty { + BindingType::Option(ty) => *ty, + _ => unreachable!("source of match some should be an option"), + } + } + + Binding::MatchVariant { + source, + variant, + field, + } => { + // Lookup type ID for the underlying enum. + let source_binding = lookup_binding(*source); + let source_ty = binding_type(&source_binding, term_id, prog, lookup_binding); + let source_type_id = match source_ty { + BindingType::Base(type_id) => type_id, + _ => unreachable!("source of match variant should be a base type"), + }; + + // Lookup variant. + let enum_ty = &prog.tyenv.types[source_type_id.index()]; + let variant = match enum_ty { + Type::Enum { variants, .. } => &variants[variant.index()], + _ => unreachable!("source match variant should be an enum"), + }; + + // Lookup field type. + BindingType::Base(field_type_by_index(&variant.fields, field.index())) + } + + Binding::ExtractStruct { source, field } => { + // Lookup type ID for the underlying struct. + let source_binding = lookup_binding(*source); + let source_ty = binding_type(&source_binding, term_id, prog, lookup_binding); + let source_type_id = match source_ty { + BindingType::Base(type_id) => type_id, + _ => unreachable!("source of extract_struct should be a base type"), + }; + + // Lookup field type. + let struct_ty = &prog.tyenv.types[source_type_id.index()]; + let fields = match struct_ty { + Type::Struct { fields, .. } => fields, + _ => unreachable!("source of extract_struct should be a struct"), + }; + BindingType::Base(field_type_by_index(fields, field.index())) + } + + Binding::MatchTuple { source, field } => { + let source_binding = lookup_binding(*source); + let source_ty = binding_type(&source_binding, term_id, prog, lookup_binding); + match source_ty { + BindingType::Tuple(tys) => tys[field.index()].clone(), + _ => unreachable!("source type should be a tuple"), + } + } + + Binding::Iterator { .. } => unimplemented!("iterator bindings not supported"), + } +} + +fn external_sig_return_type(sig: &ExternalSig) -> BindingType { + // Multiple return types are represented as a tuple. + let ty = if sig.ret_tys.len() == 1 { + BindingType::Base(sig.ret_tys[0]) + } else { + BindingType::Tuple(sig.ret_tys.iter().copied().map(BindingType::Base).collect()) + }; + + // Fallible terms return option type. + match sig.ret_kind { + ReturnKind::Option => BindingType::Option(Box::new(ty)), + ReturnKind::Plain => ty, + ReturnKind::Iterator => unimplemented!("extractor iterator return"), + } +} diff --git a/cranelift/isle/veri/veri/src/type_inference.rs b/cranelift/isle/veri/veri/src/type_inference.rs new file mode 100644 index 000000000000..9d40cd35cdee --- /dev/null +++ b/cranelift/isle/veri/veri/src/type_inference.rs @@ -0,0 +1,1249 @@ +use std::{ + cmp::Ordering, + collections::{HashMap, hash_map::Entry}, + iter::zip, + vec, +}; + +use anyhow::{Result, bail, format_err}; +use cranelift_isle::{files::Files, sema::TermId}; + +use crate::{ + spec::Signature, + types::{Compound, Const, Type, Width}, + veri::{Call, Conditions, Expr, ExprId, Qualifier, Symbolic}, +}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TypeValue { + Type(Type), + Value(Const), +} + +impl TypeValue { + pub fn ty(&self) -> Type { + match self { + TypeValue::Type(ty) => ty.clone(), + TypeValue::Value(c) => c.ty(), + } + } + + fn as_value(&self) -> Option<&Const> { + match self { + TypeValue::Value(c) => Some(c), + _ => None, + } + } + + pub fn refines_type(&self, ty: &Type) -> bool { + self >= &Self::Type(ty.clone()) + } + + pub fn merge(left: &Self, right: &Self) -> Option { + match left.partial_cmp(right) { + Some(Ordering::Greater) => Some(left.clone()), + Some(Ordering::Less | Ordering::Equal) => Some(right.clone()), + None => None, + } + } +} + +impl PartialOrd for TypeValue { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (TypeValue::Type(l), TypeValue::Type(r)) => l.partial_cmp(r), + (TypeValue::Type(ty), TypeValue::Value(v)) if ty <= &v.ty() => Some(Ordering::Less), + (TypeValue::Value(v), TypeValue::Type(ty)) if &v.ty() >= ty => Some(Ordering::Greater), + (TypeValue::Value(l), TypeValue::Value(r)) if l == r => Some(Ordering::Equal), + _ => None, + } + } +} + +impl std::fmt::Display for TypeValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TypeValue::Type(ty) => ty.fmt(f), + TypeValue::Value(c) => c.fmt(f), + } + } +} + +/// Boolean expression or its negation. +#[derive(Debug, Clone)] +pub enum Literal { + Var(ExprId), + Not(ExprId), +} + +impl std::fmt::Display for Literal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Literal::Var(x) => write!(f, "{}", x.index()), + Literal::Not(x) => write!(f, "\u{00AC}{}", x.index()), + } + } +} + +#[derive(Debug, Clone)] +pub enum Constraint { + /// Expression x has the given type. + Type { x: ExprId, ty: Type }, + /// Expressions have the same type. + SameType { x: ExprId, y: ExprId }, + /// Expressions have the same type and value. + Identical { x: ExprId, y: ExprId }, + /// Expression x is a bitvector with width given by the integer expression w. + WidthOf { x: ExprId, w: ExprId }, + /// Bitvector x is the concatenation bitvectors l and r. + Concat { x: ExprId, l: ExprId, r: ExprId }, + /// Expression x has known constant value v. + Value { x: ExprId, c: Const }, + /// Constraint conditioned on a boolean. + Implies { c: ExprId, then: Box }, + /// Clause is a disjunction that must hold. + Clause { literals: Vec }, +} + +impl std::fmt::Display for Constraint { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Constraint::Type { x, ty } => write!(f, "type({}) = {ty}", x.index()), + Constraint::SameType { x, y } => { + write!(f, "type({}) == type({})", x.index(), y.index()) + } + Constraint::Identical { x, y } => write!(f, "{} == {}", x.index(), y.index()), + Constraint::WidthOf { x, w } => write!(f, "{} = width_of({})", w.index(), x.index()), + Constraint::Concat { x, l, r } => { + write!(f, "{} = {}:{}", x.index(), l.index(), r.index()) + } + Constraint::Value { x, c } => write!(f, "{} = value({c})", x.index()), + Constraint::Implies { c, then } => write!(f, "{} => {then}", c.index()), + Constraint::Clause { literals } => write!( + f, + "clause({})", + literals + .iter() + .map(ToString::to_string) + .collect::>() + .join(" \u{2228} ") + ), + } + } +} + +#[derive(Clone)] +pub enum Choice { + TermInstantiation(TermId, Signature), +} + +impl std::fmt::Display for Choice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Choice::TermInstantiation(term_id, sig) => { + write!(f, "term_instantiation({}, {sig})", term_id.index()) + } + } + } +} + +#[derive(Clone)] +pub struct Arm { + choice: Choice, + constraints: Vec, +} + +#[derive(Default, Clone)] +pub struct Branch { + arms: Vec, +} + +#[derive(Default)] +pub struct System { + choices: Vec, + constraints: Vec, + branches: Vec, +} + +impl System { + fn fork(&self) -> Vec { + let mut branches = self.branches.clone(); + let branch = branches.pop().expect("should have at least one branch"); + + let mut children = Vec::new(); + for arm in &branch.arms { + let mut constraints = self.constraints.clone(); + constraints.extend(arm.constraints.iter().cloned()); + + let mut choices = self.choices.clone(); + // Only record the choice if there are multiple branches. + if branch.arms.len() > 1 { + choices.push(arm.choice.clone()); + } + + children.push(System { + constraints, + choices, + branches: branches.clone(), + }) + } + + children + } + + pub fn pretty_print(&self) { + println!("system {{"); + + // Choices + println!("\tchoices = ["); + for choice in &self.choices { + println!("\t\t{choice}"); + } + println!("\t]"); + + // Constraints + println!("\tconstraints = ["); + for constraint in &self.constraints { + println!("\t\t{constraint}"); + } + println!("\t]"); + + // Branches + for branch in &self.branches { + println!("\tbranch {{"); + for arm in &branch.arms { + println!("\t\t{choice} => [", choice = arm.choice); + for constraint in &arm.constraints { + println!("\t\t\t{constraint}"); + } + println!("\t\t]"); + } + println!("\t}}"); + } + + println!("}}"); + } +} + +pub fn type_constraint_system(conditions: &Conditions) -> System { + let builder = SystemBuilder::new(conditions); + builder.build() +} + +struct SystemBuilder<'a> { + conditions: &'a Conditions, + + system: System, + arm: Option, +} + +impl<'a> SystemBuilder<'a> { + fn new(conditions: &'a Conditions) -> Self { + Self { + conditions, + system: System::default(), + arm: None, + } + } + + fn build(mut self) -> System { + // Expression constraints. + for (i, expr) in self.conditions.exprs.iter().enumerate() { + self.veri_expr(ExprId(i), expr); + } + + // Assumptions. + for a in &self.conditions.assumptions { + self.boolean_value(*a, true); + } + + // Assertions. + for a in &self.conditions.assertions { + self.boolean(*a); + } + + // Calls. + for call in &self.conditions.calls { + self.call(call); + } + + // Qualifiers. + for qualifier in &self.conditions.qualifiers { + self.qualifier(qualifier); + } + + self.system + } + + fn veri_expr(&mut self, x: ExprId, expr: &Expr) { + match expr { + Expr::Const(c) => { + self.value(x, c.clone()); + } + Expr::Variable(v) => { + let ty = self.conditions.variables[v.index()].ty.clone(); + self.ty(x, ty); + } + Expr::Not(y) => { + self.boolean(x); + self.boolean(*y); + + // ((NOT X) OR (NOT Y)) + self.clause(vec![Literal::Not(x), Literal::Not(*y)]); + // (X OR Y) + self.clause(vec![Literal::Var(x), Literal::Var(*y)]); + } + Expr::And(y, z) => { + // TODO(mbm): clause implies boolean + self.boolean(x); + self.boolean(*y); + self.boolean(*z); + + // ((NOT X) OR Y) + self.clause(vec![Literal::Not(x), Literal::Var(*y)]); + // ((NOT X) OR Z) + self.clause(vec![Literal::Not(x), Literal::Var(*z)]); + // (X OR (NOT Y) OR (NOT Z)) + self.clause(vec![Literal::Var(x), Literal::Not(*y), Literal::Not(*z)]); + } + Expr::Or(y, z) => { + self.boolean(x); + self.boolean(*y); + self.boolean(*z); + + // ((NOT X) OR Y OR Z) + self.clause(vec![Literal::Not(x), Literal::Var(*y), Literal::Var(*z)]); + // (X OR (NOT Y)) + self.clause(vec![Literal::Var(x), Literal::Not(*y)]); + // (X OR (NOT Z)) + self.clause(vec![Literal::Var(x), Literal::Not(*z)]); + } + Expr::Imp(y, z) => { + self.boolean(x); + self.boolean(*y); + self.boolean(*z); + + // ((NOT X) OR (NOT Y) OR Z) + self.clause(vec![Literal::Not(x), Literal::Not(*y), Literal::Var(*z)]); + // (X OR Y) + self.clause(vec![Literal::Var(x), Literal::Var(*y)]); + // (X OR (NOT Z)) + self.clause(vec![Literal::Var(x), Literal::Not(*y)]); + } + Expr::Eq(y, z) => { + self.boolean(x); + self.same_type(*y, *z); + self.constraint(Constraint::Implies { + c: x, + then: Box::new(Constraint::Identical { x: *y, y: *z }), + }); + } + Expr::Lt(y, z) | Expr::Lte(y, z) => { + self.boolean(x); + self.integer(*y); + self.integer(*z); + } + Expr::BVUgt(y, z) + | Expr::BVUge(y, z) + | Expr::BVUlt(y, z) + | Expr::BVUle(y, z) + | Expr::BVSgt(y, z) + | Expr::BVSge(y, z) + | Expr::BVSlt(y, z) + | Expr::BVSle(y, z) + | Expr::FPEq(y, z) + | Expr::FPNe(y, z) + | Expr::FPLt(y, z) + | Expr::FPGt(y, z) + | Expr::FPLe(y, z) + | Expr::FPGe(y, z) + | Expr::BVSaddo(y, z) => { + self.boolean(x); + self.bit_vector(*y); + self.bit_vector(*z); + + self.same_type(*y, *z); + } + Expr::BVNot(y) | Expr::BVNeg(y) => { + self.bit_vector(x); + self.bit_vector(*y); + + self.same_type(x, *y); + } + Expr::Cls(y) | Expr::Clz(y) | Expr::Rev(y) | Expr::Popcnt(y) => { + self.bit_vector(x); + self.bit_vector(*y); + + self.same_type(x, *y); + } + Expr::Add(y, z) | Expr::Sub(y, z) | Expr::Mul(y, z) => { + self.integer(x); + self.integer(*y); + self.integer(*z); + } + Expr::BVAdd(y, z) + | Expr::BVSub(y, z) + | Expr::BVMul(y, z) + | Expr::BVSDiv(y, z) + | Expr::BVUDiv(y, z) + | Expr::BVSRem(y, z) + | Expr::BVURem(y, z) + | Expr::BVAnd(y, z) + | Expr::BVOr(y, z) + | Expr::BVXor(y, z) + | Expr::BVShl(y, z) + | Expr::BVLShr(y, z) + | Expr::BVAShr(y, z) + | Expr::BVRotl(y, z) + | Expr::BVRotr(y, z) + | Expr::FPAdd(y, z) + | Expr::FPSub(y, z) + | Expr::FPMul(y, z) + | Expr::FPDiv(y, z) + | Expr::FPMin(y, z) + | Expr::FPMax(y, z) => { + self.bit_vector(x); + self.bit_vector(*y); + self.bit_vector(*z); + + self.same_type(x, *y); + self.same_type(x, *z); + } + Expr::FPNeg(y) + | Expr::FPSqrt(y) + | Expr::FPCeil(y) + | Expr::FPFloor(y) + | Expr::FPNearest(y) + | Expr::FPTrunc(y) => { + self.bit_vector(x); + self.bit_vector(*y); + + self.same_type(x, *y); + } + Expr::FPIsZero(y) + | Expr::FPIsInfinite(y) + | Expr::FPIsNaN(y) + | Expr::FPIsNegative(y) + | Expr::FPIsPositive(y) => { + self.boolean(x); + self.bit_vector(*y); + } + Expr::Conditional(c, t, e) => { + self.boolean(*c); + self.same_type(x, *t); + self.same_type(x, *e); + } + Expr::BVZeroExt(w, y) | Expr::BVSignExt(w, y) | Expr::BVConvTo(w, y) => { + self.bit_vector(x); + self.integer(*w); + self.bit_vector(*y); + self.width_of(x, *w); + } + Expr::BVExtract(h, l, y) => { + let width = 1 + h + .checked_sub(*l) + .expect("high bit should not be less than low bit"); + self.bit_vector_of_width(x, width); + self.bit_vector(*y); + } + Expr::BVConcat(y, z) => { + self.bit_vector(x); + self.bit_vector(*y); + self.bit_vector(*z); + self.concat(x, *y, *z); + } + Expr::Int2BV(w, y) => { + self.bit_vector(x); + self.integer(*w); + self.integer(*y); + self.width_of(x, *w); + } + Expr::BV2Nat(y) => { + self.integer(x); + self.bit_vector(*y); + } + Expr::ToFP(w, y) | Expr::ToFPUnsigned(w, y) => { + self.integer(*w); + self.bit_vector(*y); + self.bit_vector(x); + self.width_of(x, *w); + self.width_of(*y, *w); + } + Expr::FPToUBV(w, y) | Expr::FPToSBV(w, y) => { + self.integer(*w); + self.bit_vector(*y); + self.bit_vector(x); + self.width_of(x, *w); + } + Expr::ToFPFromFP(w, y) => { + self.integer(*w); + self.bit_vector(*y); + self.bit_vector(x); + self.width_of(x, *w); + } + Expr::WidthOf(y) => { + self.integer(x); + self.bit_vector(*y); + self.width_of(*y, x); + } + Expr::FPPositiveInfinity(w) + | Expr::FPNegativeInfinity(w) + | Expr::FPPositiveZero(w) + | Expr::FPNegativeZero(w) + | Expr::FPNaN(w) => { + self.bit_vector(x); + self.integer(*w); + self.width_of(x, *w); + } + } + } + + fn call(&mut self, call: &Call) { + if call.signatures.is_empty() { + return; + } + + // Branch for the choice of term signature. + // + // We do this even for the case of a single signature, since it will + // preserve metadata about where the type assignment came from. + self.branch(); + + for sig in &call.signatures { + // Branch arm for + self.push_arm(Choice::TermInstantiation(call.term, sig.clone())); + + // Arguments. + assert_eq!(call.args.len(), sig.args.len()); + for (a, ty) in zip(&call.args, &sig.args) { + self.symbolic(a, ty.clone()); + } + + // Return. + self.symbolic(&call.ret, sig.ret.clone()); + + // Pop branch arm. + self.pop(); + } + } + + fn qualifier(&mut self, qualifier: &Qualifier) { + self.symbolic(&qualifier.value, qualifier.ty.clone()); + } + + fn symbolic(&mut self, v: &Symbolic, ty: Compound) { + match (v, ty) { + (Symbolic::Scalar(x), Compound::Primitive(ty)) => self.ty(*x, ty), + (Symbolic::Struct(fields), Compound::Struct(field_tys)) => { + assert_eq!(fields.len(), field_tys.len()); + for (field, field_ty) in zip(fields, field_tys) { + assert_eq!(field.name, field_ty.name.0); + self.symbolic(&field.value, field_ty.ty); + } + } + (Symbolic::Enum(e), Compound::Enum(enum_ty)) => { + assert_eq!(e.ty, enum_ty.id); + // Discriminant is an integer. + self.integer(e.discriminant); + // Variant types. + assert_eq!(e.variants.len(), enum_ty.variants.len()); + for (variant, variant_ty) in zip(&e.variants, &enum_ty.variants) { + assert_eq!(variant.id, variant_ty.id); + self.symbolic(&variant.value, variant_ty.ty()); + } + } + (Symbolic::Option(_), _) => unimplemented!("option types unsupported"), + (Symbolic::Tuple(_), _) => unimplemented!("tuple types unsupported"), + (v, ty) => unreachable!("type mismatch: {v} of type {ty}"), + } + } + + fn bit_vector_of_width(&mut self, x: ExprId, width: usize) { + self.ty(x, Type::BitVector(Width::Bits(width))); + } + + fn bit_vector(&mut self, x: ExprId) { + self.ty(x, Type::BitVector(Width::Unknown)); + } + + fn integer(&mut self, x: ExprId) { + self.ty(x, Type::Int); + } + + fn boolean(&mut self, x: ExprId) { + self.ty(x, Type::Bool); + } + + fn ty(&mut self, x: ExprId, ty: Type) { + self.constraint(Constraint::Type { x, ty }); + } + + fn same_type(&mut self, x: ExprId, y: ExprId) { + self.constraint(Constraint::SameType { x, y }); + } + + fn width_of(&mut self, x: ExprId, w: ExprId) { + self.constraint(Constraint::WidthOf { x, w }); + } + + fn concat(&mut self, x: ExprId, l: ExprId, r: ExprId) { + self.constraint(Constraint::Concat { x, l, r }); + } + + fn boolean_value(&mut self, x: ExprId, b: bool) { + self.value(x, Const::Bool(b)); + } + + fn value(&mut self, x: ExprId, c: Const) { + self.constraint(Constraint::Value { x, c }); + } + + fn clause(&mut self, literals: Vec) { + self.constraint(Constraint::Clause { literals }) + } + + fn constraint(&mut self, constraint: Constraint) { + let current = match self.arm.as_mut() { + Some(arm) => &mut arm.constraints, + None => &mut self.system.constraints, + }; + current.push(constraint) + } + + fn branch(&mut self) { + self.system.branches.push(Branch::default()); + } + + fn push_arm(&mut self, choice: Choice) { + assert!(self.arm.is_none()); + self.arm = Some(Arm { + choice, + constraints: Vec::new(), + }); + } + + fn pop(&mut self) { + let arm = self.arm.take().expect("must have arm"); + self.system + .branches + .last_mut() + .expect("should have branch") + .arms + .push(arm); + } +} + +#[derive(Default, Clone)] +pub struct Assignment { + pub expr_type_value: HashMap, +} + +impl Assignment { + pub fn new() -> Self { + Self { + expr_type_value: HashMap::new(), + } + } + + pub fn is_concrete(&self) -> bool { + self.expr_type_value + .values() + .all(|tv| tv.ty().is_concrete()) + } + + /// Expressions whose inferred type is not fully concrete. These are the + /// expressions responsible for an [`Status::Underconstrained`] result, + /// returned sorted by expression id for deterministic reporting. + pub fn underconstrained(&self) -> Vec { + let mut exprs: Vec = self + .expr_type_value + .iter() + .filter(|(_, tv)| !tv.ty().is_concrete()) + .map(|(x, _)| *x) + .collect(); + exprs.sort(); + exprs + } + + pub fn satisfies_constraints(&self, constraints: &[Constraint]) -> Result<()> { + constraints + .iter() + .try_for_each(|c| self.satisfies_constraint(c)) + } + + pub fn satisfies_constraint(&self, constraint: &Constraint) -> Result<()> { + match *constraint { + Constraint::Type { x, ref ty } => self.expect_expr_type_refinement(x, ty), + Constraint::SameType { x, y } => self.expect_same_type(x, y), + Constraint::Identical { x, y } => self.expect_identical(x, y), + Constraint::WidthOf { x, w } => self.expect_width_of(x, w), + Constraint::Concat { x, l, r } => self.expect_concat(x, l, r), + Constraint::Value { x, ref c } => self.expect_value(x, c), + Constraint::Implies { c, ref then } => self.expect_implies(c, then), + Constraint::Clause { ref literals } => self.expect_clause(literals), + } + } + + pub fn assignment(&self, x: ExprId) -> Option<&TypeValue> { + self.expr_type_value.get(&x) + } + + pub fn try_assignment(&self, x: ExprId) -> Result<&TypeValue> { + self.assignment(x).ok_or(format_err!( + "expression {x} missing assignment", + x = x.index() + )) + } + + pub fn value(&self, x: ExprId) -> Option<&Const> { + self.assignment(x)?.as_value() + } + + pub fn try_value(&self, x: ExprId) -> Result<&Const> { + self.value(x).ok_or(format_err!( + "expression {x} should be a known value", + x = x.index() + )) + } + + pub fn bool_value(&self, x: ExprId) -> Option { + self.value(x)?.as_bool() + } + + pub fn int_value(&self, x: ExprId) -> Option { + self.value(x)?.as_int() + } + + pub fn try_int_value(&self, x: ExprId) -> Result { + self.int_value(x).ok_or(format_err!( + "expression {x} should be a known integer value", + x = x.index() + )) + } + + pub fn literal(&self, lit: &Literal) -> Option { + match *lit { + Literal::Var(x) => self.bool_value(x), + Literal::Not(x) => Some(!self.bool_value(x)?), + } + } + + fn expect_expr_type_refinement(&self, x: ExprId, base: &Type) -> Result<()> { + let tv = self.try_assignment(x)?; + if !tv.refines_type(base) { + bail!("expected type {tv} to be refinement of {base}") + } + Ok(()) + } + + fn expect_same_type(&self, x: ExprId, y: ExprId) -> Result<()> { + let tx = self.try_assignment(x)?.ty(); + let ty = self.try_assignment(y)?.ty(); + if tx != ty { + bail!( + "expressions {x} and {y} should have same type: got {tx} and {ty}", + x = x.index(), + y = y.index() + ) + } + Ok(()) + } + + fn expect_identical(&self, x: ExprId, y: ExprId) -> Result<()> { + let tvx = self.try_assignment(x)?; + let tvy = self.try_assignment(y)?; + if tvx != tvy { + bail!( + "expressions {x} and {y} should be identical: got {tvx} and {tvy}", + x = x.index(), + y = y.index() + ) + } + Ok(()) + } + + pub fn bit_vector_width(&self, x: ExprId) -> Option { + self.assignment(x)?.ty().as_bit_vector_width()?.as_bits() + } + + pub fn try_bit_vector_width(&self, x: ExprId) -> Result { + self.bit_vector_width(x).ok_or(format_err!( + "expression {x} should be a bit-vector of known width", + x = x.index() + )) + } + + fn expect_width_of(&self, x: ExprId, w: ExprId) -> Result<()> { + // Expression x should be a concrete bitvector. + let width = self.try_bit_vector_width(x)?; + + // Expression w should be an integer equal to the width. + self.expect_value(w, &Const::Int(width.try_into().unwrap()))?; + + Ok(()) + } + + fn expect_concat(&self, x: ExprId, l: ExprId, r: ExprId) -> Result<()> { + // All inputs should be bitvectors of known width. + let x_width = self.try_bit_vector_width(x)?; + let l_width = self.try_bit_vector_width(l)?; + let r_width = self.try_bit_vector_width(r)?; + + // Verify x width is the sum of input widths. + let concat_width = l_width + .checked_add(r_width) + .expect("concat width should not overflow"); + if x_width != concat_width { + bail!( + "expression {x} should be the concatenation of {l} and {r}", + x = x.index(), + l = l.index(), + r = r.index() + ); + } + + Ok(()) + } + + fn expect_value(&self, x: ExprId, expect: &Const) -> Result<()> { + let got = self.try_value(x)?; + if got != expect { + bail!("expected value {expect}; got {got}"); + } + Ok(()) + } + + fn expect_implies(&self, c: ExprId, then: &Constraint) -> Result<()> { + if self.bool_value(c) == Some(true) { + self.satisfies_constraint(then) + } else { + Ok(()) + } + } + + fn expect_clause(&self, literals: &[Literal]) -> Result<()> { + for literal in literals { + match self.literal(literal) { + Some(true) | None => { + return Ok(()); + } + Some(false) => { + continue; + } + } + } + bail!("false clause"); + } + + pub fn pretty_print(&self, conditions: &Conditions) { + for (i, expr) in conditions.exprs.iter().enumerate() { + print!("{i}:\t"); + match self.expr_type_value.get(&ExprId(i)) { + None => print!("false\t-"), + Some(tv) => print!("{}\t{tv}", tv.ty().is_concrete()), + } + println!("\t{expr}"); + } + } +} + +pub struct Conflict { + pub x: ExprId, + pub reason: String, +} + +impl Conflict { + fn new(x: ExprId, reason: String) -> Self { + Self { x, reason } + } + + pub fn diagnostic(&self, conditions: &Conditions, files: &Files) -> String { + if let Some(pos) = conditions.pos.get(&self.x) { + format!( + "{position}: {reason}", + position = pos.pretty_print_line(files), + reason = self.reason + ) + } else { + self.reason.clone() + } + } +} + +pub enum Status { + Solved, + Inapplicable(Conflict), + Underconstrained, + TypeError(Conflict), +} + +impl std::fmt::Display for Status { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Status::Solved => write!(f, "solved"), + Status::Inapplicable(..) => write!(f, "inapplicable"), + Status::Underconstrained => write!(f, "underconstrained"), + Status::TypeError(..) => write!(f, "type error"), + } + } +} + +pub struct Solution { + pub status: Status, + pub choices: Vec, + pub assignment: Assignment, +} + +#[derive(Clone)] +pub struct Solver { + assignment: Assignment, +} + +impl Default for Solver { + fn default() -> Self { + Self::new() + } +} + +impl Solver { + pub fn new() -> Self { + Self { + assignment: Assignment::new(), + } + } + + pub fn solve(mut self, system: &System) -> Vec { + // Deduce assignments from constraints. + let result = self.propagate(&system.constraints); + if let Err(status) = result { + return vec![Solution { + status, + choices: system.choices.clone(), + assignment: self.assignment, + }]; + } + + // Done? + if system.branches.is_empty() { + let status = if self.assignment.is_concrete() { + Status::Solved + } else { + Status::Underconstrained + }; + return vec![Solution { + status, + choices: system.choices.clone(), + assignment: self.assignment, + }]; + }; + + // Fork. + let mut solutions = Vec::new(); + for child in system.fork() { + let sub = self.clone(); + solutions.extend(sub.solve(&child)); + } + + solutions + } + + fn propagate(&mut self, constraints: &[Constraint]) -> Result<(), Status> { + // Iterate until no changes. + while self.iterate(constraints)? {} + Ok(()) + } + + fn iterate(&mut self, constraints: &[Constraint]) -> Result { + let mut change = false; + for constraint in constraints { + // TODO(mbm): remove satisfied constraints from list + change |= self.constraint(constraint)?; + } + Ok(change) + } + + fn constraint(&mut self, constraint: &Constraint) -> Result { + log::trace!("process type constraint: {constraint}"); + match constraint { + Constraint::Type { x, ty } => self.set_type(*x, ty.clone()), + Constraint::SameType { x, y } => self.same_type(*x, *y), + Constraint::Identical { x, y } => self.identical(*x, *y), + Constraint::WidthOf { x, w } => self.width_of(*x, *w), + Constraint::Concat { x, l, r } => self.concat(*x, *l, *r), + Constraint::Value { x, c } => self.set_value(*x, c.clone()), + Constraint::Implies { c, then } => self.implies(*c, then), + Constraint::Clause { literals } => self.clause(literals), + } + } + + fn set_type_value(&mut self, x: ExprId, tv: TypeValue) -> Result { + log::trace!("set type value: {x:?} = {tv:?}"); + + // If we don't have an assignment for the expression, record it. + if let Entry::Vacant(e) = self.assignment.expr_type_value.entry(x) { + e.insert(tv); + return Ok(true); + } + + // If we do, merge this type value with the existing one. + let existing = &self.assignment.expr_type_value[&x]; + let merged = TypeValue::merge(existing, &tv).ok_or_else(|| { + if !existing.ty().is_compatible_with(&tv.ty()) { + Status::TypeError(Conflict::new( + x, + format!("concrete type error between types:\n\t{existing}\n\t{tv}"), + )) + } else { + Status::Inapplicable(Conflict::new( + x, + format!("inapplicable set type value: {existing:?} = {tv:?}"), + )) + } + })?; + if merged != *existing { + self.assignment.expr_type_value.insert(x, merged); + return Ok(true); + } + + // No change. + Ok(false) + } + + fn set_type(&mut self, x: ExprId, ty: Type) -> Result { + self.set_type_value(x, TypeValue::Type(ty)) + } + + fn set_bit_vector_width(&mut self, x: ExprId, bits: usize) -> Result { + self.set_type(x, Type::BitVector(Width::Bits(bits))) + } + + fn same_type(&mut self, x: ExprId, y: ExprId) -> Result { + // TODO(mbm): union find + // TODO(mbm): simplify by initializing all expression types to unknown + match ( + self.assignment.expr_type_value.get(&x).cloned(), + self.assignment.expr_type_value.get(&y).cloned(), + ) { + (None, None) => Ok(false), + (Some(tvx), None) => self.set_type(y, tvx.ty()), + (None, Some(tvy)) => self.set_type(x, tvy.ty()), + (Some(tvx), Some(tvy)) => Ok(self.set_type(x, tvy.ty())? | self.set_type(y, tvx.ty())?), + } + } + + fn identical(&mut self, x: ExprId, y: ExprId) -> Result { + match ( + self.assignment.expr_type_value.get(&x).cloned(), + self.assignment.expr_type_value.get(&y).cloned(), + ) { + (None, None) => Ok(false), + (Some(tvx), None) => self.set_type_value(y, tvx), + (None, Some(tvy)) => self.set_type_value(x, tvy), + (Some(tvx), Some(tvy)) => { + Ok(self.set_type_value(x, tvy)? | self.set_type_value(y, tvx)?) + } + } + } + + fn width_of(&mut self, x: ExprId, w: ExprId) -> Result { + match ( + self.assignment.expr_type_value.get(&x), + self.assignment.expr_type_value.get(&w), + ) { + ( + Some( + &TypeValue::Type(Type::BitVector(Width::Bits(width))) + | &TypeValue::Value(Const::BitVector(width, _)), + ), + _, + ) => self.set_int_value(w, width.try_into().unwrap()), + (_, Some(&TypeValue::Value(Const::Int(v)))) => { + self.set_bit_vector_width(x, v.try_into().unwrap()) + } + _ => Ok(false), + } + } + + fn concat(&mut self, x: ExprId, l: ExprId, r: ExprId) -> Result { + match ( + self.assignment.bit_vector_width(x), + self.assignment.bit_vector_width(l), + self.assignment.bit_vector_width(r), + ) { + // Two known: we can infer the third. + (None, Some(lw), Some(rw)) => { + // Width equation: |x| = |l| + |r| + self.set_bit_vector_width(x, lw + rw) + } + (Some(xw), None, Some(rw)) => { + // Width equation: |l| = |x| - |r| + self.set_bit_vector_width( + l, + xw.checked_sub(rw).ok_or_else(|| { + Status::Inapplicable(Conflict::new( + l, + format!("inapplicable concat xw - rw: {l:?} = {r:?}"), + )) + })?, + ) + } + (Some(xw), Some(lw), None) => { + // Width equation: |r| = |x| - |l| + self.set_bit_vector_width( + r, + xw.checked_sub(lw).ok_or_else(|| { + Status::Inapplicable(Conflict::new( + r, + format!("inapplicable concat xw - lw: {l:?} = {r:?}"), + )) + })?, + ) + } + + // Zero or one known: cannot deduce anything. + (None, None, None) + | (None, None, Some(_)) + | (None, Some(_), None) + | (Some(_), None, None) => Ok(false), + + // All known: verify correctness. + (Some(xw), Some(lw), Some(rw)) => { + if xw != lw + rw { + Err(Status::Inapplicable(Conflict::new( + x, + format!("inapplicable concat known: {l:?} = {r:?}"), + ))) + } else { + Ok(false) + } + } + } + } + + fn implies(&mut self, c: ExprId, then: &Constraint) -> Result { + if self.assignment.bool_value(c) == Some(true) { + self.constraint(then) + } else { + Ok(false) + } + } + + fn clause(&mut self, literals: &[Literal]) -> Result { + // Check if we can propogate the value of a single unknown literal. + let mut unknown = None; + for literal in literals { + match (self.assignment.literal(literal), unknown) { + // One disjunction is known true. Can't deduce anything else. + (Some(true), _) => { + return Ok(false); + } + // Known false: also deduce nothing. + (Some(false), _) => { + continue; + } + // First unknown literal. + (None, None) => { + unknown = Some(literal); + } + // More than one unknown literal: deduce nothing. + (None, Some(_)) => { + return Ok(false); + } + } + } + + // Assign true. + match unknown { + Some(lit) => self.set_literal(lit, true), + None => Ok(false), + } + } + + fn set_literal(&mut self, lit: &Literal, b: bool) -> Result { + match *lit { + Literal::Var(x) => self.set_bool_value(x, b), + Literal::Not(x) => self.set_bool_value(x, !b), + } + } + + fn set_bool_value(&mut self, x: ExprId, b: bool) -> Result { + self.set_value(x, Const::Bool(b)) + } + + fn set_int_value(&mut self, x: ExprId, v: i128) -> Result { + self.set_value(x, Const::Int(v)) + } + + fn set_value(&mut self, x: ExprId, c: Const) -> Result { + self.set_type_value(x, TypeValue::Value(c)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testing::{assert_partial_order_properties, assert_strictly_increasing}; + + #[test] + fn test_type_value_partial_order_bit_vector() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unknown), + TypeValue::Type(Type::BitVector(Width::Unknown)), + TypeValue::Type(Type::BitVector(Width::Bits(64))), + TypeValue::Value(Const::BitVector(64, 42u8.into())), + ]); + } + + #[test] + fn test_type_value_partial_order_int() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unknown), + TypeValue::Type(Type::Int), + TypeValue::Value(Const::Int(42)), + ]); + } + + #[test] + fn test_type_value_partial_order_bool() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unknown), + TypeValue::Type(Type::Bool), + TypeValue::Value(Const::Bool(true)), + ]); + } + + #[test] + fn test_type_value_partial_order_unspecified() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unspecified), + TypeValue::Value(Const::Unspecified), + ]); + } + + #[test] + fn test_type_value_partial_order_properties() { + assert_partial_order_properties(&[ + // Unknown + TypeValue::Type(Type::Unknown), + // BitVectors + TypeValue::Type(Type::BitVector(Width::Unknown)), + TypeValue::Type(Type::BitVector(Width::Bits(32))), + TypeValue::Value(Const::BitVector(32, 42u8.into())), + TypeValue::Value(Const::BitVector(32, 43u8.into())), + TypeValue::Type(Type::BitVector(Width::Bits(64))), + TypeValue::Value(Const::BitVector(64, 42u8.into())), + TypeValue::Value(Const::BitVector(64, 43u8.into())), + // Int + TypeValue::Type(Type::Int), + TypeValue::Value(Const::Int(42)), + TypeValue::Value(Const::Int(43)), + // Bool + TypeValue::Type(Type::Bool), + TypeValue::Value(Const::Bool(false)), + TypeValue::Value(Const::Bool(true)), + // Unspecified + TypeValue::Type(Type::Unspecified), + TypeValue::Value(Const::Unspecified), + ]); + } +} diff --git a/cranelift/isle/veri/veri/src/types.rs b/cranelift/isle/veri/veri/src/types.rs new file mode 100644 index 000000000000..925c997597f5 --- /dev/null +++ b/cranelift/isle/veri/veri/src/types.rs @@ -0,0 +1,487 @@ +use std::cmp::Ordering; + +use anyhow::Result; +use cranelift_isle::{ + ast::{Ident, ModelType}, + lexer::Pos, + sema::{self, BuiltinType, Sym, TypeEnv, TypeId, VariantId}, +}; +use num_bigint::BigUint; + +/// Width of a bit vector. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Width { + Unknown, + Bits(usize), +} + +impl Width { + pub fn as_bits(&self) -> Option { + match self { + Width::Unknown => None, + Width::Bits(bits) => Some(*bits), + } + } +} + +impl PartialOrd for Width { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Width::Unknown, Width::Unknown) => Some(Ordering::Equal), + (Width::Unknown, Width::Bits(_)) => Some(Ordering::Less), + (Width::Bits(_), Width::Unknown) => Some(Ordering::Greater), + (Width::Bits(l), Width::Bits(r)) if l == r => Some(Ordering::Equal), + (Width::Bits(_), Width::Bits(_)) => None, + } + } +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Type { + Unspecified, + Unknown, + BitVector(Width), + Int, + Bool, + Unit, +} + +impl Type { + pub fn is_concrete(&self) -> bool { + match self { + Type::Unspecified => true, + Type::Unknown | Type::BitVector(Width::Unknown) => false, + Type::BitVector(Width::Bits(_)) | Type::Int | Type::Bool | Type::Unit => true, + } + } + + pub fn as_bit_vector_width(&self) -> Option<&Width> { + match self { + Type::BitVector(w) => Some(w), + _ => None, + } + } + + pub fn is_compatible_with(&self, other: &Type) -> bool { + matches!( + (self, other), + (Type::Unknown, _) + | (_, Type::Unknown) + | (Type::Unspecified, Type::Unspecified) + | (Type::Unit, Type::Unit) + | (Type::Bool, Type::Bool) + | (Type::Int, Type::Int) + | (Type::BitVector(_), Type::BitVector(_)) + ) + } +} + +impl std::fmt::Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Type::Unspecified => write!(f, "\u{2a33}"), + Type::Unknown => write!(f, "unk"), + Type::BitVector(Width::Bits(w)) => write!(f, "bv {w}"), + Type::BitVector(Width::Unknown) => write!(f, "bv _"), + Type::Int => write!(f, "int"), + Type::Bool => write!(f, "bool"), + Type::Unit => write!(f, "unit"), + } + } +} + +impl PartialOrd for Type { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + // Unspecified is equal to itself, but otherwise incomparible. + (Type::Unspecified, Type::Unspecified) => Some(Ordering::Equal), + (Type::Unspecified, _) | (_, Type::Unspecified) => None, + + (Type::Unknown, Type::Unknown) => Some(Ordering::Equal), + (Type::Unknown, _) => Some(Ordering::Less), + (_, Type::Unknown) => Some(Ordering::Greater), + (Type::BitVector(l), Type::BitVector(r)) => l.partial_cmp(r), + (Type::Int, Type::Int) => Some(Ordering::Equal), + (Type::Bool, Type::Bool) => Some(Ordering::Equal), + (Type::Unit, Type::Unit) => Some(Ordering::Equal), + (_, _) => None, + } + } +} + +#[derive(Debug, Clone)] +pub enum Compound { + Primitive(Type), + Struct(Vec), + Enum(Enum), + // TODO(mbm): intern name identifier + Named(Ident), +} + +#[derive(Debug, Clone)] +pub struct Field { + // TODO(mbm): intern name identifier + pub name: Ident, + pub ty: Compound, +} + +impl Field { + fn from_struct_field(field: &sema::StructField, tyenv: &TypeEnv) -> Self { + let ty = &tyenv.types[field.ty.index()]; + Self { + name: Ident(tyenv.syms[field.name.index()].clone(), Pos::default()), + ty: Compound::named_from_isle(ty, tyenv), + } + } + + fn from_tuple_field(index: usize, field: &sema::TupleField, tyenv: &TypeEnv) -> Self { + let ty = &tyenv.types[field.ty.index()]; + Self { + name: Ident(index.to_string(), Pos::default()), + ty: Compound::named_from_isle(ty, tyenv), + } + } + + pub fn from_isle_fields(fields: &sema::Fields, tyenv: &TypeEnv) -> Vec { + match fields { + sema::Fields::Unit => Vec::new(), + sema::Fields::Struct(s) => s + .fields + .iter() + .map(|f| Self::from_struct_field(f, tyenv)) + .collect(), + sema::Fields::Tuple(t) => t + .fields + .iter() + .enumerate() + .map(|(i, f)| Self::from_tuple_field(i, f, tyenv)) + .collect(), + } + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + Ok(Field { + name: self.name.clone(), + ty: self.ty.resolve(lookup)?, + }) + } +} + +/// Look up the name of a field in an ISLE `Fields` by index. For tuple fields, +/// the synthesized name matches the convention used elsewhere (the index as a +/// decimal string). +pub fn field_name_by_index(fields: &sema::Fields, index: usize, tyenv: &TypeEnv) -> String { + match fields { + sema::Fields::Unit => panic!("unit fields cannot be indexed"), + sema::Fields::Struct(s) => tyenv.syms[s.fields[index].name.index()].clone(), + sema::Fields::Tuple(_) => index.to_string(), + } +} + +/// Look up the type of a field in an ISLE `Fields` by index. +pub fn field_type_by_index(fields: &sema::Fields, index: usize) -> TypeId { + match fields { + sema::Fields::Unit => panic!("unit fields cannot be indexed"), + sema::Fields::Struct(s) => s.fields[index].ty, + sema::Fields::Tuple(t) => t.fields[index].ty, + } +} + +#[derive(Debug, Clone)] +pub struct Variant { + pub name: Ident, + pub id: VariantId, + pub fields: Vec, +} + +impl Variant { + fn from_isle(variant: &sema::Variant, tyenv: &TypeEnv) -> Self { + Self { + name: Ident(tyenv.syms[variant.name.index()].clone(), variant.pos), + id: variant.id, + fields: Field::from_isle_fields(&variant.fields, tyenv), + } + } + + pub fn is_unit(&self) -> bool { + self.fields.is_empty() + } + + pub fn ty(&self) -> Compound { + Compound::Struct(self.fields.clone()) + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + Ok(Variant { + name: self.name.clone(), + id: self.id, + fields: self + .fields + .iter() + .map(|f| f.resolve(lookup)) + .collect::>()?, + }) + } +} + +impl std::fmt::Display for Variant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_unit() { + write!(f, "{name}", name = self.name.0) + } else { + write!(f, "{name} {ty}", name = self.name.0, ty = self.ty()) + } + } +} + +#[derive(Debug, Clone)] +pub struct Enum { + pub name: Ident, + pub id: TypeId, + pub variants: Vec, +} + +impl Enum { + pub fn from_isle( + name: Sym, + id: TypeId, + variants: &[sema::Variant], + pos: Pos, + tyenv: &TypeEnv, + ) -> Self { + Self { + name: Ident(tyenv.syms[name.index()].clone(), pos), + id, + variants: variants + .iter() + .map(|v| Variant::from_isle(v, tyenv)) + .collect(), + } + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + Ok(Self { + name: self.name.clone(), + id: self.id, + variants: self + .variants + .iter() + .map(|v| v.resolve(lookup)) + .collect::>()?, + }) + } +} + +impl Compound { + pub fn from_ast(model: &ModelType) -> Self { + match model { + ModelType::Unspecified => Self::Primitive(Type::Unspecified), + ModelType::Auto => Self::Primitive(Type::Unknown), + ModelType::Int => Self::Primitive(Type::Int), + ModelType::Bool => Self::Primitive(Type::Bool), + ModelType::Unit => Self::Primitive(Type::Unit), + ModelType::BitVec(None) => Self::Primitive(Type::BitVector(Width::Unknown)), + ModelType::BitVec(Some(bits)) => Self::Primitive(Type::BitVector(Width::Bits(*bits))), + ModelType::Struct(fields) => Self::Struct( + fields + .iter() + .map(|m| Field { + name: m.name.clone(), + ty: Self::from_ast(&m.ty), + }) + .collect(), + ), + ModelType::Named(name) => Self::Named(name.clone()), + } + } + + /// Derive a type corresponding to the given ISLE type, if possible. For + /// ISLE internal enumerations, this will build the corresponding VeriISLE + /// enum representation. + pub fn from_isle(ty: &sema::Type, tyenv: &TypeEnv) -> Option { + match ty { + sema::Type::Enum { + name, + id, + variants, + pos, + .. + } if !variants.is_empty() => Some(Self::Enum(Enum::from_isle( + *name, *id, variants, *pos, tyenv, + ))), + sema::Type::Struct { fields, .. } => { + Some(Self::Struct(Field::from_isle_fields(fields, tyenv))) + } + _ => None, + } + } + + /// Build a named reference to the given ISLE type. + pub fn named_from_isle(ty: &sema::Type, tyenv: &TypeEnv) -> Self { + match ty { + sema::Type::Builtin(BuiltinType::Bool) => Self::Primitive(Type::Bool), + sema::Type::Builtin(b) => Self::Primitive(Type::BitVector(Width::Bits(b.to_usize()))), + _ => Self::Named(Ident( + ty.name(tyenv).to_string(), + ty.pos().expect("expected position"), + )), + } + } + + pub fn as_primitive(&self) -> Option<&Type> { + match self { + Compound::Primitive(ty) => Some(ty), + _ => None, + } + } + + pub fn as_enum(&self) -> Option<&Enum> { + match self { + Compound::Enum(e) => Some(e), + _ => None, + } + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + match self { + Compound::Primitive(_) => Ok(self.clone()), + Compound::Struct(fields) => Ok(Compound::Struct( + fields + .iter() + .map(|f| f.resolve(lookup)) + .collect::>()?, + )), + Compound::Enum(e) => Ok(Compound::Enum(e.resolve(lookup)?)), + Compound::Named(name) => { + // TODO(mbm): named type model cycle detection + let ty = lookup(name)?; + ty.resolve(lookup) + } + } + } +} + +impl std::fmt::Display for Compound { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Compound::Primitive(ty) => ty.fmt(f), + Compound::Struct(fields) => write!( + f, + "{{{fields}}}", + fields = fields + .iter() + .map(|f| format!("{}: {}", f.name.0, f.ty)) + .collect::>() + .join(", ") + ), + Compound::Enum(e) => { + write!(f, "enum({name})", name = e.name.0,) + } + Compound::Named(name) => write!(f, "{}", name.0), + } + } +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Const { + Bool(bool), + Int(i128), + BitVector(usize, BigUint), + Unspecified, +} + +impl Const { + pub fn ty(&self) -> Type { + match self { + Const::Bool(_) => Type::Bool, + Const::Int(_) => Type::Int, + Const::BitVector(w, _) => Type::BitVector(Width::Bits(*w)), + Const::Unspecified => Type::Unspecified, + } + } + + pub fn as_bool(&self) -> Option { + match self { + Const::Bool(b) => Some(*b), + _ => None, + } + } + + pub fn as_int(&self) -> Option { + match self { + Const::Int(v) => Some(*v), + _ => None, + } + } +} + +impl std::fmt::Display for Const { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Const::Bool(b) => write!(f, "{b}"), + Const::Int(v) => write!(f, "{v}"), + Const::BitVector(bits, v) => { + if bits % 4 == 0 { + write!(f, "#x{v:0>nibbles$x}", nibbles = bits / 4) + } else { + write!(f, "#b{v:0>bits$b}") + } + } + Const::Unspecified => write!(f, "\u{2a33}"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testing::assert_partial_order_properties; + + #[test] + fn test_width_partial_order_less_than() { + assert!(Width::Unknown < Width::Bits(64)); + } + + #[test] + fn test_width_partial_order_properties() { + assert_partial_order_properties(&[Width::Unknown, Width::Bits(32), Width::Bits(64)]); + } + + #[test] + fn test_type_partial_order_less_than() { + assert!(Type::Unknown < Type::BitVector(Width::Unknown)); + assert!(Type::BitVector(Width::Unknown) < Type::BitVector(Width::Bits(64))); + assert!(Type::Unknown < Type::Int); + assert!(Type::Unknown < Type::Bool); + } + + #[test] + fn test_type_partial_order_properties() { + assert_partial_order_properties(&[ + Type::Unspecified, + Type::Unknown, + Type::BitVector(Width::Unknown), + Type::BitVector(Width::Bits(32)), + Type::BitVector(Width::Bits(64)), + Type::Int, + Type::Bool, + Type::Unit, + ]); + } +} diff --git a/cranelift/isle/veri/veri/src/veri.rs b/cranelift/isle/veri/veri/src/veri.rs new file mode 100644 index 000000000000..b02a56eaffd2 --- /dev/null +++ b/cranelift/isle/veri/veri/src/veri.rs @@ -0,0 +1,2510 @@ +use crate::{ + expand::{Constrain, Expansion}, + program::Program, + spec::{self, Arm, Constructor, Signature, State}, + trie::{BindingType, binding_type}, + types::{Compound, Const, Type, Variant, Width, field_name_by_index}, +}; +use anyhow::{Context, Error, Result, bail, format_err}; +use cranelift_isle::{ + ast::Ident, + lexer::Pos, + sema::{self, Sym, TermId, TypeId, VariantId}, + trie_again::{Binding, BindingId, Constraint, TupleIndex}, +}; +use std::{ + collections::{HashMap, HashSet, hash_map::Entry}, + iter::zip, +}; + +thread_local!(pub static DBG_DEPTH: std::cell::Cell = const { std::cell::Cell::new(0) }); +pub struct DbgGuard; +impl Drop for DbgGuard { + fn drop(&mut self) { + DBG_DEPTH.with(|d| d.set(d.get() - 1)); + } +} +macro_rules! dbg_depth { + ($name:expr) => { + let _g = DbgGuard; + let __d = DBG_DEPTH.with(|d| { + let n = d.get() + 1; + d.set(n); + n + }); + if __d % 200 == 0 { + eprintln!("DBG combined depth={} at {}", __d, $name); + } + }; +} + +declare_id!( + /// The id of an expression within verification Conditions. + #[must_use] + ExprId +); + +declare_id!( + /// The id of a variable within verification Conditions. + VariableId +); + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Expr { + // Terminals. + Const(Const), + Variable(VariableId), + + // Boolean. + Not(ExprId), + And(ExprId, ExprId), + Or(ExprId, ExprId), + Imp(ExprId, ExprId), + Eq(ExprId, ExprId), + Lt(ExprId, ExprId), + Lte(ExprId, ExprId), + + BVUgt(ExprId, ExprId), + BVUge(ExprId, ExprId), + BVUlt(ExprId, ExprId), + BVUle(ExprId, ExprId), + + BVSgt(ExprId, ExprId), + BVSge(ExprId, ExprId), + BVSlt(ExprId, ExprId), + BVSle(ExprId, ExprId), + + BVSaddo(ExprId, ExprId), + + // Unary. + BVNot(ExprId), + BVNeg(ExprId), + Cls(ExprId), + Clz(ExprId), + Rev(ExprId), + Popcnt(ExprId), + + // Binary. + Add(ExprId, ExprId), + Sub(ExprId, ExprId), + Mul(ExprId, ExprId), + BVAdd(ExprId, ExprId), + BVSub(ExprId, ExprId), + BVMul(ExprId, ExprId), + BVSDiv(ExprId, ExprId), + BVUDiv(ExprId, ExprId), + BVSRem(ExprId, ExprId), + BVURem(ExprId, ExprId), + BVAnd(ExprId, ExprId), + BVOr(ExprId, ExprId), + BVXor(ExprId, ExprId), + BVShl(ExprId, ExprId), + BVLShr(ExprId, ExprId), + BVAShr(ExprId, ExprId), + BVRotl(ExprId, ExprId), + BVRotr(ExprId, ExprId), + + // ITE + Conditional(ExprId, ExprId, ExprId), + + // Bitwidth conversion. + BVZeroExt(ExprId, ExprId), + BVSignExt(ExprId, ExprId), + BVConvTo(ExprId, ExprId), + + // Extract specified bit range. + BVExtract(usize, usize, ExprId), + + // Concatenate bitvectors. + BVConcat(ExprId, ExprId), + + // Integer conversion. + Int2BV(ExprId, ExprId), + BV2Nat(ExprId), + + // Bitwidth. + WidthOf(ExprId), + + // Floating point conversion. + ToFP(ExprId, ExprId), + ToFPUnsigned(ExprId, ExprId), + ToFPFromFP(ExprId, ExprId), + FPToUBV(ExprId, ExprId), + FPToSBV(ExprId, ExprId), + + // Floating point. + FPPositiveInfinity(ExprId), + FPNegativeInfinity(ExprId), + FPPositiveZero(ExprId), + FPNegativeZero(ExprId), + FPNaN(ExprId), + FPEq(ExprId, ExprId), + FPNe(ExprId, ExprId), + FPLt(ExprId, ExprId), + FPGt(ExprId, ExprId), + FPLe(ExprId, ExprId), + FPGe(ExprId, ExprId), + FPAdd(ExprId, ExprId), + FPSub(ExprId, ExprId), + FPMul(ExprId, ExprId), + FPDiv(ExprId, ExprId), + FPMin(ExprId, ExprId), + FPMax(ExprId, ExprId), + FPNeg(ExprId), + FPCeil(ExprId), + FPFloor(ExprId), + FPSqrt(ExprId), + FPTrunc(ExprId), + FPNearest(ExprId), + FPIsZero(ExprId), + FPIsInfinite(ExprId), + FPIsNaN(ExprId), + FPIsNegative(ExprId), + FPIsPositive(ExprId), +} + +impl Expr { + pub fn is_variable(&self) -> bool { + matches!(self, Self::Variable(_)) + } + + pub fn pure(&self) -> bool { + !matches!(self, Expr::BVConvTo(..)) + } + + pub fn sources(&self) -> Vec { + match self { + Expr::Const(_) | Expr::Variable(_) => Vec::new(), + // Unary + Expr::Not(x) + | Expr::BVNot(x) + | Expr::BVNeg(x) + | Expr::BVExtract(_, _, x) + | Expr::BV2Nat(x) + | Expr::Cls(x) + | Expr::Clz(x) + | Expr::Rev(x) + | Expr::Popcnt(x) + | Expr::WidthOf(x) + | Expr::FPPositiveInfinity(x) + | Expr::FPNegativeInfinity(x) + | Expr::FPPositiveZero(x) + | Expr::FPNegativeZero(x) + | Expr::FPNaN(x) + | Expr::FPNeg(x) + | Expr::FPCeil(x) + | Expr::FPFloor(x) + | Expr::FPSqrt(x) + | Expr::FPTrunc(x) + | Expr::FPNearest(x) + | Expr::FPIsZero(x) + | Expr::FPIsInfinite(x) + | Expr::FPIsNaN(x) + | Expr::FPIsNegative(x) + | Expr::FPIsPositive(x) => vec![*x], + + // Binary + Expr::And(x, y) + | Expr::Or(x, y) + | Expr::Imp(x, y) + | Expr::Eq(x, y) + | Expr::Lt(x, y) + | Expr::Lte(x, y) + | Expr::Add(x, y) + | Expr::Sub(x, y) + | Expr::Mul(x, y) + | Expr::BVUgt(x, y) + | Expr::BVUge(x, y) + | Expr::BVUlt(x, y) + | Expr::BVUle(x, y) + | Expr::BVSgt(x, y) + | Expr::BVSge(x, y) + | Expr::BVSlt(x, y) + | Expr::BVSle(x, y) + | Expr::BVSaddo(x, y) + | Expr::BVAdd(x, y) + | Expr::BVSub(x, y) + | Expr::BVMul(x, y) + | Expr::BVSDiv(x, y) + | Expr::BVUDiv(x, y) + | Expr::BVSRem(x, y) + | Expr::BVURem(x, y) + | Expr::BVAnd(x, y) + | Expr::BVOr(x, y) + | Expr::BVXor(x, y) + | Expr::BVShl(x, y) + | Expr::BVLShr(x, y) + | Expr::BVAShr(x, y) + | Expr::BVRotl(x, y) + | Expr::BVRotr(x, y) + | Expr::BVZeroExt(x, y) + | Expr::BVSignExt(x, y) + | Expr::BVConvTo(x, y) + | Expr::Int2BV(x, y) + | Expr::ToFP(x, y) + | Expr::ToFPUnsigned(x, y) + | Expr::ToFPFromFP(x, y) + | Expr::FPToUBV(x, y) + | Expr::FPToSBV(x, y) + | Expr::BVConcat(x, y) + | Expr::FPEq(x, y) + | Expr::FPNe(x, y) + | Expr::FPLt(x, y) + | Expr::FPGt(x, y) + | Expr::FPLe(x, y) + | Expr::FPGe(x, y) + | Expr::FPAdd(x, y) + | Expr::FPSub(x, y) + | Expr::FPMul(x, y) + | Expr::FPDiv(x, y) + | Expr::FPMin(x, y) + | Expr::FPMax(x, y) => vec![*x, *y], + + // Ternary + Expr::Conditional(c, t, e) => vec![*c, *t, *e], + } + } +} + +impl std::fmt::Display for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Expr::Const(c) => write!(f, "const({c})"), + Expr::Variable(v) => write!(f, "var({})", v.index()), + Expr::Not(x) => write!(f, "!{}", x.index()), + Expr::And(x, y) => write!(f, "{} && {}", x.index(), y.index()), + Expr::Or(x, y) => write!(f, "{} || {}", x.index(), y.index()), + Expr::Imp(x, y) => write!(f, "{} => {}", x.index(), y.index()), + Expr::Eq(x, y) => write!(f, "{} == {}", x.index(), y.index()), + Expr::Lt(x, y) => write!(f, "{} < {}", x.index(), y.index()), + Expr::Lte(x, y) => write!(f, "{} <= {}", x.index(), y.index()), + Expr::Add(x, y) => write!(f, "{} + {}", x.index(), y.index()), + Expr::Sub(x, y) => write!(f, "{} - {}", x.index(), y.index()), + Expr::Mul(x, y) => write!(f, "{} * {}", x.index(), y.index()), + Expr::BVUgt(x, y) => write!(f, "bvugt({}, {})", x.index(), y.index()), + Expr::BVUge(x, y) => write!(f, "bvuge({}, {})", x.index(), y.index()), + Expr::BVUlt(x, y) => write!(f, "bvult({}, {})", x.index(), y.index()), + Expr::BVUle(x, y) => write!(f, "bvule({}, {})", x.index(), y.index()), + Expr::BVSgt(x, y) => write!(f, "bvsgt({}, {})", x.index(), y.index()), + Expr::BVSge(x, y) => write!(f, "bvsge({}, {})", x.index(), y.index()), + Expr::BVSlt(x, y) => write!(f, "bvslt({}, {})", x.index(), y.index()), + Expr::BVSle(x, y) => write!(f, "bvsle({}, {})", x.index(), y.index()), + Expr::BVSaddo(x, y) => write!(f, "bvsaddo({}, {})", x.index(), y.index()), + Expr::BVNot(x) => write!(f, "bvnot({})", x.index()), + Expr::BVNeg(x) => write!(f, "bvneg({})", x.index()), + Expr::Cls(x) => write!(f, "cls({})", x.index()), + Expr::Clz(x) => write!(f, "clz({})", x.index()), + Expr::Rev(x) => write!(f, "rev({})", x.index()), + Expr::Popcnt(x) => write!(f, "popcnt({})", x.index()), + Expr::BVAdd(x, y) => write!(f, "bvadd({}, {})", x.index(), y.index()), + Expr::BVSub(x, y) => write!(f, "bvsub({}, {})", x.index(), y.index()), + Expr::BVMul(x, y) => write!(f, "bvmul({}, {})", x.index(), y.index()), + Expr::BVSDiv(x, y) => write!(f, "bvsdiv({}, {})", x.index(), y.index()), + Expr::BVUDiv(x, y) => write!(f, "bvudiv({}, {})", x.index(), y.index()), + Expr::BVSRem(x, y) => write!(f, "bvsrem({}, {})", x.index(), y.index()), + Expr::BVURem(x, y) => write!(f, "bvurem({}, {})", x.index(), y.index()), + Expr::BVAnd(x, y) => write!(f, "bvand({}, {})", x.index(), y.index()), + Expr::BVOr(x, y) => write!(f, "bvor({}, {})", x.index(), y.index()), + Expr::BVXor(x, y) => write!(f, "bvxor({}, {})", x.index(), y.index()), + Expr::BVShl(x, y) => write!(f, "bvshl({}, {})", x.index(), y.index()), + Expr::BVLShr(x, y) => write!(f, "bvlshr({}, {})", x.index(), y.index()), + Expr::BVAShr(x, y) => write!(f, "bvashr({}, {})", x.index(), y.index()), + Expr::BVRotl(x, y) => write!(f, "bvrotl({}, {})", x.index(), y.index()), + Expr::BVRotr(x, y) => write!(f, "bvrotr({}, {})", x.index(), y.index()), + Expr::Conditional(c, t, e) => { + write!(f, "{} ? {} : {}", c.index(), t.index(), e.index()) + } + Expr::BVZeroExt(w, x) => write!(f, "bv_zero_ext({}, {})", w.index(), x.index()), + Expr::BVSignExt(w, x) => write!(f, "bv_zero_ext({}, {})", w.index(), x.index()), + Expr::BVConvTo(w, x) => write!(f, "bv_conv_to({}, {})", w.index(), x.index()), + Expr::BVExtract(h, l, x) => write!(f, "bv_extract({h}, {l}, {})", x.index()), + Expr::BVConcat(x, y) => write!(f, "bv_concat({}, {})", x.index(), y.index()), + Expr::Int2BV(w, x) => write!(f, "int2bv({}, {})", w.index(), x.index()), + Expr::ToFP(w, x) => write!(f, "to_fp({}, {})", w.index(), x.index()), + Expr::ToFPUnsigned(w, x) => write!(f, "to_fp_unsigned({}, {})", w.index(), x.index()), + Expr::ToFPFromFP(w, x) => write!(f, "to_fp_from_fp({}, {})", w.index(), x.index()), + Expr::FPToUBV(w, x) => write!(f, "fp.to_ubv({}, {})", w.index(), x.index()), + Expr::FPToSBV(w, x) => write!(f, "fp.to_sbv({}, {})", w.index(), x.index()), + Expr::BV2Nat(x) => write!(f, "bv2nat({})", x.index()), + Expr::WidthOf(x) => write!(f, "width_of({})", x.index()), + Expr::FPPositiveInfinity(x) => write!(f, "fp.+oo({})", x.index()), + Expr::FPNegativeInfinity(x) => write!(f, "fp.-oo({})", x.index()), + Expr::FPPositiveZero(x) => write!(f, "fp.+zero({})", x.index()), + Expr::FPNegativeZero(x) => write!(f, "fp.-zero({})", x.index()), + Expr::FPNaN(x) => write!(f, "fp.NaN({})", x.index()), + Expr::FPEq(x, y) => write!(f, "fp.eq({}, {})", x.index(), y.index()), + Expr::FPNe(x, y) => write!(f, "fp.ne({}, {})", x.index(), y.index()), + Expr::FPLt(x, y) => write!(f, "fp.lt({}, {})", x.index(), y.index()), + Expr::FPGt(x, y) => write!(f, "fp.gt({}, {})", x.index(), y.index()), + Expr::FPLe(x, y) => write!(f, "fp.le({}, {})", x.index(), y.index()), + Expr::FPGe(x, y) => write!(f, "fp.ge({}, {})", x.index(), y.index()), + Expr::FPAdd(x, y) => write!(f, "fp.add({}, {})", x.index(), y.index()), + Expr::FPSub(x, y) => write!(f, "fp.sub({}, {})", x.index(), y.index()), + Expr::FPMul(x, y) => write!(f, "fp.mul({}, {})", x.index(), y.index()), + Expr::FPDiv(x, y) => write!(f, "fp.div({}, {})", x.index(), y.index()), + Expr::FPMin(x, y) => write!(f, "fp.min({}, {})", x.index(), y.index()), + Expr::FPMax(x, y) => write!(f, "fp.max({}, {})", x.index(), y.index()), + Expr::FPNeg(x) => write!(f, "fp.neg({})", x.index()), + Expr::FPCeil(x) => write!(f, "fp.ceil({})", x.index()), + Expr::FPFloor(x) => write!(f, "fp.floor({})", x.index()), + Expr::FPSqrt(x) => write!(f, "fp.sqrt({})", x.index()), + Expr::FPTrunc(x) => write!(f, "fp.trunc({})", x.index()), + Expr::FPNearest(x) => write!(f, "fp.nearest({})", x.index()), + Expr::FPIsZero(x) => write!(f, "fp.isZero({})", x.index()), + Expr::FPIsInfinite(x) => write!(f, "fp.isInfinite({})", x.index()), + Expr::FPIsNaN(x) => write!(f, "fp.isNaN({})", x.index()), + Expr::FPIsNegative(x) => write!(f, "fp.isNegative({})", x.index()), + Expr::FPIsPositive(x) => write!(f, "fp.isPositive({})", x.index()), + } + } +} + +pub type Model = HashMap; + +#[derive(Debug)] +pub struct Variable { + pub ty: Type, + pub name: String, +} + +impl Variable { + fn component_name(prefix: &str, field: &str) -> String { + format!("{prefix}_{field}") + } +} + +#[derive(Debug, Clone)] +pub struct SymbolicOption { + some: ExprId, + inner: Box, +} + +#[derive(Debug, Clone)] +pub struct SymbolicField { + pub name: String, + pub value: Symbolic, +} + +impl SymbolicField { + fn eval(&self, model: &Model) -> Result { + Ok(FieldValue { + name: self.name.clone(), + value: self.value.eval(model)?, + }) + } +} + +#[derive(Debug, Clone)] +pub struct SymbolicEnum { + pub ty: TypeId, + pub discriminant: ExprId, + pub variants: Vec, +} + +impl SymbolicEnum { + fn try_variant_by_name(&self, name: &str) -> Result<&SymbolicVariant> { + self.variants + .iter() + .find(|v| v.name == name) + .ok_or(format_err!("no variant with name {name}")) + } + + fn validate(&self) -> Result<()> { + // Expect the variants to have distinct discriminants in the range [0, num_variants). + for (expect, variant) in self.variants.iter().enumerate() { + if variant.discriminant != expect { + bail!( + "variant '{name}' has unexpected discriminant", + name = variant.name + ); + } + } + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub struct SymbolicVariant { + pub name: String, + pub id: VariantId, + pub discriminant: usize, + pub value: Symbolic, +} + +impl SymbolicVariant { + fn try_field_by_name(&self, name: &str) -> Result<&SymbolicField> { + self.fields()? + .iter() + .find(|f| f.name == name) + .ok_or(format_err!("no field with name {name}")) + } + + fn field_values(&self) -> Result> { + Ok(self.fields()?.iter().map(|f| f.value.clone()).collect()) + } + + fn fields(&self) -> Result<&Vec> { + self.value + .as_struct() + .ok_or(format_err!("variant value is not a struct")) + } +} + +impl std::fmt::Display for SymbolicVariant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{name} {value}", name = self.name, value = self.value) + } +} + +/// Inline spec expression macro. +/// +/// Note that at this stage the spec expressions are preserved as +/// [`spec::Expr`]. Generation of [`Expr`] objects from them is deferred until +/// macro expansion. +#[derive(Debug, Clone)] +pub struct Macro { + pub params: Vec, + pub body: spec::Expr, +} + +#[derive(Debug, Clone)] +pub enum Symbolic { + Scalar(ExprId), + Struct(Vec), + Enum(SymbolicEnum), + Option(SymbolicOption), + Tuple(Vec), + Macro(Macro), +} + +impl Symbolic { + fn as_scalar(&self) -> Option { + match self { + Self::Scalar(x) => Some(*x), + _ => None, + } + } + + fn as_struct(&self) -> Option<&Vec> { + match self { + Self::Struct(fields) => Some(fields), + _ => None, + } + } + + fn as_enum(&self) -> Option<&SymbolicEnum> { + match self { + Self::Enum(e) => Some(e), + _ => None, + } + } + + fn as_option(&self) -> Option<&SymbolicOption> { + match self { + Self::Option(opt) => Some(opt), + _ => None, + } + } + + fn as_tuple(&self) -> Option<&Vec> { + match self { + Self::Tuple(fields) => Some(fields), + _ => None, + } + } + + fn elements(&self) -> &[Symbolic] { + match self { + Self::Tuple(fields) => &fields[..], + v => std::slice::from_ref(v), + } + } + + fn eval(&self, model: &Model) -> Result { + match self { + Symbolic::Scalar(x) => Ok(Value::Const( + model + .get(x) + .ok_or(format_err!("undefined expression in model"))? + .clone(), + )), + Symbolic::Struct(fields) => Ok(Value::Struct( + fields + .iter() + .map(|f| f.eval(model)) + .collect::>()?, + )), + Symbolic::Enum(e) => { + // Determine the enum variant by looking up the discriminant. + let discriminant: usize = model + .get(&e.discriminant) + .ok_or(format_err!("undefined discriminant in model"))? + .as_int() + .ok_or(format_err!( + "model value for discriminant is not an integer" + ))? + .try_into() + .unwrap(); + let variant = e + .variants + .iter() + .find(|v| v.discriminant == discriminant) + .ok_or(format_err!("no variant with discriminant {discriminant}"))?; + Ok(Value::Enum(Box::new(VariantValue { + name: variant.name.clone(), + value: variant.value.eval(model)?, + }))) + } + Symbolic::Option(opt) => match model.get(&opt.some) { + Some(Const::Bool(true)) => { + Ok(Value::Option(Some(Box::new(opt.inner.eval(model)?)))) + } + Some(Const::Bool(false)) => Ok(Value::Option(None)), + Some(_) => bail!("model value for option some is not boolean"), + None => bail!("undefined expression in model"), + }, + Symbolic::Tuple(elements) => Ok(Value::Tuple( + elements + .iter() + .map(|s| s.eval(model)) + .collect::>()?, + )), + Symbolic::Macro(_) => bail!("cannot evaluate macro"), + } + } + + // Build a new value by applying the given map function to all constituent + // scalars in this symbolic value. + fn scalar_map(&self, f: &mut F) -> Symbolic + where + F: FnMut(ExprId) -> ExprId, + { + match self { + Symbolic::Scalar(x) => Symbolic::Scalar(f(*x)), + Symbolic::Struct(fields) => Symbolic::Struct( + fields + .iter() + .map(|field| SymbolicField { + name: field.name.clone(), + value: field.value.scalar_map(f), + }) + .collect(), + ), + Symbolic::Enum(e) => Symbolic::Enum(SymbolicEnum { + ty: e.ty, + discriminant: f(e.discriminant), + variants: e + .variants + .iter() + .map(|v| SymbolicVariant { + id: v.id, + name: v.name.clone(), + discriminant: v.discriminant, + value: v.value.scalar_map(f), + }) + .collect(), + }), + v => todo!("scalar map: {v:?}"), + } + } + + fn merge(a: &Symbolic, b: &Symbolic, merge: &mut F) -> Result + where + F: FnMut(ExprId, ExprId) -> ExprId, + { + if std::mem::discriminant(a) != std::mem::discriminant(b) { + bail!("conditional arms have incompatible types"); + } + match (a, b) { + (Symbolic::Scalar(a), Symbolic::Scalar(b)) => Ok(merge(*a, *b).into()), + (Symbolic::Struct(a_fields), Symbolic::Struct(b_fields)) => { + assert_eq!(a_fields.len(), b_fields.len()); + Ok(Symbolic::Struct( + zip(a_fields, b_fields) + .map(|(a, b)| { + assert_eq!(a.name, b.name); + Ok(SymbolicField { + name: a.name.clone(), + value: Symbolic::merge(&a.value, &b.value, merge)?, + }) + }) + .collect::>()?, + )) + } + (Symbolic::Enum(a), Symbolic::Enum(b)) => { + assert_eq!(a.ty, b.ty); + let ty = a.ty; + let discriminant = merge(a.discriminant, b.discriminant); + assert_eq!(a.variants.len(), b.variants.len()); + let variants = zip(&a.variants, &b.variants) + .map(|(a, b)| { + assert_eq!(a.name, b.name); + assert_eq!(a.id, b.id); + assert_eq!(a.discriminant, b.discriminant); + Ok(SymbolicVariant { + name: a.name.clone(), + id: a.id, + discriminant: a.discriminant, + value: Symbolic::merge(&a.value, &b.value, merge)?, + }) + }) + .collect::>()?; + Ok(Symbolic::Enum(SymbolicEnum { + ty, + discriminant, + variants, + })) + } + case => todo!("symbolic merge types: {case:?}"), + } + } +} + +impl From for Symbolic { + fn from(x: ExprId) -> Self { + Symbolic::Scalar(x) + } +} + +impl std::fmt::Display for Symbolic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Symbolic::Scalar(x) => write!(f, "{}", x.index()), + Symbolic::Struct(fields) => write!( + f, + "{{{fields}}}", + fields = fields + .iter() + .map(|f| format!("{}: {}", f.name, f.value)) + .collect::>() + .join(", ") + ), + Symbolic::Enum(e) => write!( + f, + "{{{discriminant}, {variants}}}", + discriminant = e.discriminant.index(), + variants = e + .variants + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ), + Symbolic::Option(SymbolicOption { some, inner }) => { + write!(f, "Option{{some: {}, inner: {inner}}}", some.index()) + } + Symbolic::Tuple(vs) => write!( + f, + "({vs})", + vs = vs + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ), + Symbolic::Macro(_) => write!(f, "macro"), + } + } +} + +#[derive(Clone, Debug)] +pub enum Value { + Const(Const), + Struct(Vec), + Enum(Box), + Option(Option>), + Tuple(Vec), +} + +#[derive(Debug, Clone)] +pub struct FieldValue { + name: String, + value: Value, +} + +#[derive(Debug, Clone)] +pub struct VariantValue { + name: String, + value: Value, +} + +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Value::Const(c) => c.fmt(f), + Value::Struct(fields) => write!( + f, + "{{{fields}}}", + fields = fields + .iter() + .map(|f| format!("{}: {}", f.name, f.value)) + .collect::>() + .join(", ") + ), + Value::Enum(v) => write!(f, "{name} {value}", name = v.name, value = v.value), + Value::Option(Some(v)) => write!(f, "Some({v})"), + Value::Option(None) => write!(f, "None"), + Value::Tuple(elements) => write!( + f, + "({})", + elements + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ), + } + } +} + +#[derive(Debug)] +pub struct Call { + pub term: TermId, + pub args: Vec, + pub ret: Symbolic, + pub signatures: Vec, +} + +// Type qualifier, for example derived from an `(as ...)` expression. +#[derive(Debug)] +pub struct Qualifier { + pub value: Symbolic, + pub ty: Compound, +} + +/// Verification conditions for an expansion. +#[derive(Debug, Default)] +pub struct Conditions { + pub exprs: Vec, + pub assumptions: Vec, + pub assertions: Vec, + pub variables: Vec, + pub state: Variables, + pub calls: Vec, + pub qualifiers: Vec, + pub pos: HashMap, +} + +impl Conditions { + pub fn from_expansion(expansion: &Expansion, prog: &Program) -> Result { + let builder = ConditionsBuilder::new(expansion, prog); + builder.build() + } + + pub fn pretty_print(&self, prog: &Program) { + println!("conditions {{"); + + // Expressions + println!("\texprs = ["); + for (i, expr) in self.exprs.iter().enumerate() { + println!("\t\t{i}:\t{expr}"); + } + println!("\t]"); + + // Assumptions + println!("\tassumptions = ["); + for expr_id in &self.assumptions { + println!("\t\t{}", expr_id.index()); + } + println!("\t]"); + + // Assertions + println!("\tassertions = ["); + for expr_id in &self.assertions { + println!("\t\t{}", expr_id.index()); + } + println!("\t]"); + + // Variables + println!("\tvariables = ["); + for (i, v) in self.variables.iter().enumerate() { + println!("\t\t{i}:\t{ty}\t{name}", ty = v.ty, name = v.name); + } + println!("\t]"); + + // Calls + // TODO(mbm): prettier pretty printing code + println!("\tcalls = ["); + for call in &self.calls { + println!("\t\tcall {{"); + println!("\t\t\tterm = {}", prog.term_name(call.term)); + if !call.args.is_empty() { + println!("\t\t\targs = ["); + for arg in &call.args { + println!("\t\t\t\t{}", arg); + } + println!("\t\t\t]"); + } + println!("\t\t\tret = {}", call.ret); + if !call.signatures.is_empty() { + println!("\t\t\tsignatures = ["); + for sig in &call.signatures { + println!("\t\t\t\tsignature {{"); + if !sig.args.is_empty() { + println!("\t\t\t\t\targs = ["); + for arg in &sig.args { + println!("\t\t\t\t\t\t{arg}"); + } + println!("\t\t\t\t\t]"); + } + println!("\t\t\t\t\tret = {}", sig.ret); + println!("\t\t\t\t}}"); + } + println!("\t\t\t]"); + } + println!("\t\t}}"); + } + println!("\t]"); + + println!("}}"); + } + + pub fn validate(&self) -> Result<()> { + // Ensure there are no dangling expressions. + let reachable = self.reachable(); + for x in (0..self.exprs.len()).map(ExprId) { + if self.exprs[x.index()].is_variable() { + continue; + } + if !reachable.contains(&x) { + bail!("expression {x} is unreachable", x = x.index()); + } + } + + Ok(()) + } + + fn reachable(&self) -> HashSet { + let mut reach = HashSet::new(); + + let mut stack: Vec = Vec::new(); + stack.extend(&self.assumptions); + stack.extend(&self.assertions); + + while let Some(x) = stack.pop() { + if reach.contains(&x) { + continue; + } + + reach.insert(x); + let expr = &self.exprs[x.index()]; + stack.extend(expr.sources()); + } + + reach + } + + pub fn print_model(&self, model: &Model, prog: &Program) -> Result<()> { + self.write_model(&mut std::io::stdout(), model, prog) + } + + pub fn write_model( + &self, + out: &mut dyn std::io::Write, + model: &Model, + prog: &Program, + ) -> Result<()> { + // State + for (name, value) in &self.state.0 { + writeln!(out, "state: {name} = {}", value.eval(model)?)?; + } + + // Calls + for call in &self.calls { + // Skip unit enum variant terms, which may occur frequently and are + // rarely informative. + let term = prog.term(call.term); + if term.is_enum_variant() && call.args.is_empty() { + continue; + } + + writeln!( + out, + "{term_name}({args}) -> {ret}", + term_name = prog.term_name(call.term), + args = call + .args + .iter() + .map(|a| Ok(a.eval(model)?.to_string())) + .collect::>>()? + .join(", "), + ret = call.ret.eval(model)? + )?; + } + + Ok(()) + } + + pub fn error_at_expr(&self, prog: &Program, x: ExprId, msg: impl Into) -> Error { + if let Some(pos) = self.pos.get(&x) { + prog.error_at_pos(*pos, msg).into() + } else { + Error::msg(msg.into()) + } + } +} + +enum TermKind { + Constructor, + Extractor, +} + +#[derive(Copy, Clone)] +enum Invocation { + Caller, + Callee, +} + +#[derive(Copy, Clone)] +enum Domain { + Total, + Partial(ExprId), +} + +impl Domain { + fn from_return_value(value: &Symbolic) -> (Self, Symbolic) { + match value { + Symbolic::Option(opt) => (Self::Partial(opt.some), (*opt.inner).clone()), + v => (Self::Total, v.clone()), + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct Variables(HashMap); + +impl Variables { + fn new() -> Self { + Self(HashMap::new()) + } + + fn get(&self, name: &String) -> Option<&Symbolic> { + self.0.get(name) + } + + fn expect(&self, name: &String) -> Result<&Symbolic> { + self.get(name) + .ok_or(format_err!("undefined variable {name}")) + } + + fn set(&mut self, name: String, value: Symbolic) -> Result<()> { + match self.0.entry(name) { + Entry::Occupied(e) => { + bail!("redefinition of variable {name}", name = e.key()); + } + Entry::Vacant(e) => { + e.insert(value); + Ok(()) + } + } + } +} + +struct ConditionsBuilder<'a> { + expansion: &'a Expansion, + prog: &'a Program, + + state_modification_conds: HashMap>, + binding_value: HashMap, + expr_map: HashMap, + conditions: Conditions, + position_stack: Vec, +} + +impl<'a> ConditionsBuilder<'a> { + fn new(expansion: &'a Expansion, prog: &'a Program) -> Self { + Self { + expansion, + prog, + state_modification_conds: HashMap::new(), + binding_value: HashMap::new(), + expr_map: HashMap::new(), + conditions: Conditions::default(), + position_stack: Vec::new(), + } + } + + fn build(mut self) -> Result { + // State initialization. + for state in &self.prog.specenv.state { + self.init_state(state)?; + } + + // Bindings. + for (i, binding) in self.expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + self.add_binding(i.try_into().unwrap(), binding)?; + } + } + + // Callee contract for the term under expansion. + self.constructor( + self.expansion.result, + self.expansion.term, + &self.expansion.parameters, + Invocation::Callee, + )?; + + // Constraints. + for constrain in &self.expansion.constraints { + let holds = self.constrain(constrain)?; + self.conditions.assumptions.push(holds); + } + + // Equals. + for (a, b) in self.expansion.equalities() { + let eq = self.bindings_equal(a, b)?; + self.conditions.assumptions.push(eq); + } + + // State defaults. + for state in &self.prog.specenv.state { + self.state_default(state)?; + } + + // Validate + self.conditions.validate()?; + + Ok(self.conditions) + } + + fn init_state(&mut self, state: &State) -> Result<()> { + let name = &state.name.0; + let value = self.alloc_value(&state.ty, name.clone())?; + self.conditions.state.set(name.clone(), value)?; + Ok(()) + } + + fn state_default(&mut self, state: &State) -> anyhow::Result<()> { + // Evaluate the default spec expression in a scope that only defines + // the state variable itself. + let mut vars = Variables::new(); + let name = &state.name.0; + vars.set(name.clone(), self.conditions.state.expect(name)?.clone())?; + let mut default = self.spec_expr(&state.default, &vars)?; + + // Other specs may have declared conditions under which they modify the + // state. The default only applies when none of them are true. + if let Some(conds) = self.state_modification_conds.get(name) { + let modified = self.any(conds.clone()); + let not_modified = self.dedup_expr(Expr::Not(modified)); + default = self.scalar(Expr::Imp(not_modified, self.as_scalar(default)?)); + } + + // The expression should define an assumption about the state variable, + // so should be a scalar boolean. + self.conditions.assumptions.push(self.as_scalar(default)?); + + Ok(()) + } + + fn add_binding(&mut self, id: BindingId, binding: &Binding) -> Result<()> { + dbg_depth!("add_binding"); + // Exit if already added. + if self.binding_value.contains_key(&id) { + return Ok(()); + } + + // Allocate a value. + let binding_type = self.binding_type(binding); + let name = format!("b{}", id.index()); + let value = self.alloc_binding(&binding_type, name)?; + self.binding_value.insert(id, value); + + // Ensure dependencies have been added. + for source in binding.sources() { + let source_binding = self + .expansion + .binding(*source) + .expect("source binding should be defined"); + self.add_binding(*source, source_binding)?; + } + + // Generate conditions depending on binding type. + match binding { + Binding::ConstInt { val, ty } => self.const_int(id, *val, *ty), + + Binding::ConstBool { val, .. } => self.const_bool(id, *val), + + Binding::ConstPrim { val } => self.const_prim(id, *val), + + // Argument binding has no associated constraints. + Binding::Argument { .. } => Ok(()), + + Binding::Extractor { term, parameter } => self.extractor(id, *term, *parameter), + + Binding::Constructor { + term, parameters, .. + } => self.constructor(id, *term, parameters, Invocation::Caller), + + Binding::Iterator { .. } => unimplemented!("iterator bindings"), + + Binding::MakeVariant { + ty, + variant, + fields, + } => self.make_variant(id, *ty, *variant, fields), + + Binding::MatchVariant { + source, + variant, + field, + } => self.match_variant(id, *source, *variant, *field), + + Binding::MakeStruct { ty, fields } => self.make_struct(id, *ty, fields), + + Binding::ExtractStruct { source, field } => self.extract_struct(id, *source, *field), + + Binding::MakeSome { inner } => self.make_some(id, *inner), + + Binding::MatchSome { source } => self.match_some(id, *source), + + Binding::MatchTuple { source, field } => self.match_tuple(id, *source, *field), + } + } + + fn const_int(&mut self, id: BindingId, val: i128, ty: TypeId) -> Result<()> { + let eq = self.equals_const_int(id, val, ty)?; + self.conditions.assumptions.push(eq); + Ok(()) + } + + fn const_bool(&mut self, id: BindingId, val: bool) -> Result<()> { + let eq = self.equals_const_bool(id, val)?; + self.conditions.assumptions.push(eq); + Ok(()) + } + + fn equals_const_int(&mut self, id: BindingId, val: i128, ty: TypeId) -> Result { + // Determine modeled type. + let ty_name = self.prog.type_name(ty); + let ty = self + .prog + .specenv + .type_model + .get(&ty) + .ok_or(self.error(format!("no model for type {ty_name}")))? + .as_primitive() + .ok_or(self.error("constant must have basic type"))?; + + // Construct value of the determined type. + let value = self.spec_typed_value(val, ty)?.into(); + + // Destination binding equals constant value. + let eq = self.values_equal(self.binding_value[&id].clone(), value)?; + Ok(eq) + } + + fn const_prim(&mut self, id: BindingId, val: Sym) -> Result<()> { + let eq = self.equals_const_prim(id, val)?; + self.conditions.assumptions.push(eq); + Ok(()) + } + + fn equals_const_prim(&mut self, id: BindingId, val: Sym) -> Result { + // Lookup value. + let spec_value = self + .prog + .specenv + .const_value + .get(&val) + .ok_or(self.error(format!( + "value of constant {const_name} is unspecified", + const_name = self.prog.tyenv.syms[val.index()] + )))?; + let value = self.spec_expr_no_vars(spec_value)?; + + // Destination binding equals constant value. + let eq = self.values_equal(self.binding_value[&id].clone(), value)?; + Ok(eq) + } + + fn equals_const_bool(&mut self, id: BindingId, val: bool) -> Result { + // Destination binding equals constant value. + let value = Symbolic::Scalar(self.boolean(val)); + let eq = self.values_equal(self.binding_value[&id].clone(), value)?; + Ok(eq) + } + + fn extractor(&mut self, id: BindingId, term: TermId, parameter: BindingId) -> Result<()> { + // Arguments are the actually the return values of an + // extractor, possibly wrapped in an Option<..> type. + let (domain, ret) = Domain::from_return_value(&self.binding_value[&id]); + let args = ret.elements(); + + // Result maps to the parameter of an extractor. + let result = self.binding_value[¶meter].clone(); + + // Call extractor. + self.call( + term, + TermKind::Extractor, + args, + result, + Invocation::Caller, + domain, + ) + .with_context(|| { + format!( + "expanding extractor '{name}'", + name = self.prog.term_name(term) + ) + }) + } + + fn constructor( + &mut self, + id: BindingId, + term: TermId, + parameters: &[BindingId], + invocation: Invocation, + ) -> Result<()> { + // Arguments. + let mut args = Vec::new(); + for parameter_binding_id in parameters { + let x = self + .binding_value + .get(parameter_binding_id) + .expect("parameter binding should be defined") + .clone(); + args.push(x); + } + + // Return value. + let (domain, result) = Domain::from_return_value(&self.binding_value[&id]); + + // Call constructor. + self.call( + term, + TermKind::Constructor, + &args, + result, + invocation, + domain, + ) + .with_context(|| { + format!( + "expanding constructor '{name}'", + name = self.prog.term_name(term) + ) + }) + } + + fn call( + &mut self, + term: TermId, + kind: TermKind, + args: &[Symbolic], + ret: Symbolic, + invocation: Invocation, + domain: Domain, + ) -> Result<()> { + dbg_depth!("call"); + // Lookup spec. + let term_name = self.prog.term_name(term); + let term_spec = self + .prog + .specenv + .term_spec + .get(&term) + .ok_or(self.error(format!("no spec for term {term_name}",)))?; + + // We are provided the arguments and return value as they appear + // syntactically in the term declaration and specification. However, + // whether these are the actual inputs and outputs of the corresponding + // function depends on the term kind. + if term_spec.args.len() != args.len() { + bail!("incorrect number of arguments for term {term_name}"); + } + let arguments: Vec<_> = zip(&term_spec.args, args).collect(); + let result = (&term_spec.ret, &ret); + let (inputs, outputs) = match kind { + TermKind::Constructor => (arguments.as_slice(), std::slice::from_ref(&result)), + TermKind::Extractor => (std::slice::from_ref(&result), arguments.as_slice()), + }; + + // Scope for spec expression evaluation. State variables are always available. + let mut vars = self.conditions.state.clone(); + + // State modification conditions. + for modifies in &term_spec.modifies { + let cond = if let Some(cond_name) = &modifies.cond { + // Allocate new boolean for the modification condition. + let cond = self.alloc_variable( + Type::Bool, + format!("{name}_modification_cond", name = modifies.state.0), + ); + // Bring into spec scope. + vars.set(cond_name.0.clone(), cond.into())?; + cond + } else { + // TODO(mbm): warn when state is both conditionally and unconditionaly modified. + self.boolean(true) + }; + + // Record condition to determine when the default spec applies. + self.state_modification_conds + .entry(modifies.state.0.clone()) + .or_default() + .push(cond); + } + + // Inputs are available to the requires and matches clauses. + for (name, input) in inputs { + vars.set(name.0.clone(), (*input).clone())?; + } + + // Requires. + let mut requires: Vec = Vec::new(); + for require in &term_spec.requires { + let require = self.spec_expr(require, &vars)?; + requires.push(self.as_scalar(require)?); + } + + // Matches. + let mut matches: Vec = Vec::new(); + for m in &term_spec.matches { + let m = self.spec_expr(m, &vars)?; + matches.push(self.as_scalar(m)?); + } + + // Outputs: only in scope for provides. + for (name, output) in outputs { + vars.set(name.0.clone(), (*output).clone())?; + } + + // Provides. + let mut provides: Vec = Vec::new(); + for provide in &term_spec.provides { + let provide = self.spec_expr(provide, &vars)?; + provides.push(self.as_scalar(provide)?); + } + + // Partial function. + // REVIEW(mbm): pin down semantics for partial function specifications. + if let Domain::Partial(p) = domain { + // Matches describe when the function applies. + let all_matches = self.all(matches); + let eq = self.exprs_equal(p, all_matches); + self.conditions.assumptions.push(eq); + + // Provides are conditioned on the match. + let all_provides = self.all(provides); + let provide = self.dedup_expr(Expr::Imp(all_matches, all_provides)); + provides = vec![provide]; + } else if !matches.is_empty() { + bail!("spec matches on non-partial function"); + } + + // Assert/assume depending on caller or callee. + match invocation { + Invocation::Caller => { + self.conditions.assertions.extend(requires); + self.conditions.assumptions.extend(provides); + } + Invocation::Callee => { + self.conditions.assumptions.extend(requires); + self.conditions.assertions.extend(provides); + } + } + + // Record callsite. + self.record_term_instantiation(term, args.to_vec(), ret)?; + + Ok(()) + } + + fn record_term_instantiation( + &mut self, + term: TermId, + args: Vec, + ret: Symbolic, + ) -> Result<()> { + let signatures = self + .prog + .specenv + .resolve_term_instantiations(&term, &self.prog.tyenv)?; + self.conditions.calls.push(Call { + term, + args, + ret, + signatures, + }); + Ok(()) + } + + fn make_variant( + &mut self, + id: BindingId, + ty: TypeId, + variant: VariantId, + fields: &[BindingId], + ) -> Result<()> { + // Lookup term corresponding to variant. + let variant_term_id = self.prog.get_variant_term(ty, variant); + + // Invoke as a constructor. + self.constructor(id, variant_term_id, fields, Invocation::Caller)?; + + Ok(()) + } + + fn match_variant( + &mut self, + id: BindingId, + source: BindingId, + variant: VariantId, + field: TupleIndex, + ) -> Result<()> { + // Source binding should be an enum. + let e = self.binding_value[&source] + .as_enum() + .ok_or(self.error("target of variant constraint should be an enum"))? + .clone(); + + // Lookup enum type via corresponding constriant, + let tys: Vec<_> = self + .expansion + .constraints + .iter() + .flat_map(|c| match c { + Constrain::Match(id, Constraint::Variant { ty, variant: v, .. }) + if *id == source && *v == variant => + { + Some(ty) + } + _ => None, + }) + .collect(); + if tys.len() != 1 { + bail!("expected exactly one variant constraint for match variant binding"); + } + let ty = tys[0]; + + // Lookup variant and field. + let variant_type = self.prog.tyenv.get_variant(*ty, variant); + let variant_name = self.prog.tyenv.syms[variant_type.name.index()].as_str(); + + let field_name = field_name_by_index(&variant_type.fields, field.index(), &self.prog.tyenv); + + // Destination binding. + let v = self.binding_value[&id].clone(); + + // Assumption: if the variant matches then the destination binding + // equals the projected field. + let variant = e.try_variant_by_name(variant_name)?; + let field = variant.try_field_by_name(&field_name)?; + + let discriminator = self.discriminator(&e, variant); + let eq = self.values_equal(v, field.value.clone())?; + let constraint = self.dedup_expr(Expr::Imp(discriminator, eq)); + self.conditions.assumptions.push(constraint); + + Ok(()) + } + + fn make_struct(&mut self, id: BindingId, ty: TypeId, fields: &[BindingId]) -> Result<()> { + // Destination binding should already be allocated as a struct. + let dest_fields = self.binding_value[&id] + .as_struct() + .ok_or(self.error("target of make_struct should be a struct"))? + .clone(); + + // Lookup the struct type's field list for naming. + let struct_ty = &self.prog.tyenv.types[ty.index()]; + let struct_fields = match struct_ty { + sema::Type::Struct { fields, .. } => fields, + _ => bail!("MakeStruct target type should be Type::Struct"), + }; + + if dest_fields.len() != fields.len() { + bail!("make_struct: destination field count does not match binding count"); + } + + // Each input field binding's value equals the corresponding destination field's value. + for (i, &field_binding_id) in fields.iter().enumerate() { + let field_name = field_name_by_index(struct_fields, i, &self.prog.tyenv); + let dest_field = dest_fields + .iter() + .find(|f| f.name == field_name) + .ok_or(format_err!("no field with name {field_name}"))?; + let input_value = self.binding_value[&field_binding_id].clone(); + let eq = self.values_equal(dest_field.value.clone(), input_value)?; + self.conditions.assumptions.push(eq); + } + + Ok(()) + } + + fn extract_struct( + &mut self, + id: BindingId, + source: BindingId, + field: TupleIndex, + ) -> Result<()> { + // Source binding should be a struct. + let s = self.binding_value[&source] + .as_struct() + .ok_or(self.error("source of extract_struct should be a struct"))? + .clone(); + + // Lookup the struct type via the corresponding constraint on the source. + let tys: Vec<_> = self + .expansion + .constraints + .iter() + .flat_map(|c| match c { + Constrain::Match(cid, Constraint::Struct { ty, .. }) if *cid == source => Some(ty), + _ => None, + }) + .collect(); + if tys.len() != 1 { + bail!("expected exactly one struct constraint for extract_struct binding"); + } + let ty = tys[0]; + + // Lookup field name from the struct type. + let struct_ty = &self.prog.tyenv.types[ty.index()]; + let struct_fields = match struct_ty { + sema::Type::Struct { fields, .. } => fields, + _ => bail!("source of extract_struct should be Type::Struct"), + }; + let field_name = field_name_by_index(struct_fields, field.index(), &self.prog.tyenv); + + // Locate the matching field in the source struct value. + let symbolic_field = s + .iter() + .find(|f| f.name == field_name) + .ok_or(format_err!("no field with name {field_name}"))?; + + // Assumption: destination binding equals the projected field value. + let v = self.binding_value[&id].clone(); + let eq = self.values_equal(v, symbolic_field.value.clone())?; + self.conditions.assumptions.push(eq); + + Ok(()) + } + + fn make_some(&mut self, id: BindingId, inner: BindingId) -> Result<()> { + // Destination binding should be an option. + let opt = self.binding_value[&id] + .as_option() + .expect("destination of make_some binding should be an option") + .clone(); + + // Inner binding. + let inner = self.binding_value[&inner].clone(); + + // Assumption: option is Some. + self.conditions.assumptions.push(opt.some); + + // Assumption: option value is equal to this binding. + let eq = self.values_equal(inner, (*opt.inner).clone())?; + self.conditions.assumptions.push(eq); + + Ok(()) + } + + fn match_some(&mut self, id: BindingId, source: BindingId) -> Result<()> { + // Source should be an option. + let opt = self.binding_value[&source] + .as_option() + .expect("source of match_some binding should be an option") + .clone(); + + // Destination binding. + let v = self.binding_value[&id].clone(); + + // Assumption: if the option is some, then the inner value + // equals this binding. + let eq = self.values_equal(v, (*opt.inner).clone())?; + let constraint = self.dedup_expr(Expr::Imp(opt.some, eq)); + self.conditions.assumptions.push(constraint); + + Ok(()) + } + + fn match_tuple(&mut self, id: BindingId, source: BindingId, field: TupleIndex) -> Result<()> { + // Source should be a tuple. Access its fields. + let fields = self.binding_value[&source] + .as_tuple() + .expect("source of match_tuple binding should be a tuple") + .clone(); + + // Destination binding. + let v = self.binding_value[&id].clone(); + + // Assumption: indexed field should equal this binding. + let eq = self.values_equal(v, fields[field.index()].clone())?; + self.conditions.assumptions.push(eq); + + Ok(()) + } + + fn constrain(&mut self, constrain: &Constrain) -> Result { + match constrain { + Constrain::Match(binding_id, constraint) => self.constraint(*binding_id, constraint), + Constrain::NotAll(constrains) => { + let cs = constrains + .iter() + .map(|c| self.constrain(c)) + .collect::>()?; + let all = self.all(cs); + let not_all = self.dedup_expr(Expr::Not(all)); + Ok(not_all) + } + } + } + + fn constraint(&mut self, binding_id: BindingId, constraint: &Constraint) -> Result { + match constraint { + Constraint::Some => self.constraint_some(binding_id), + Constraint::ConstPrim { val } => self.equals_const_prim(binding_id, *val), + Constraint::ConstBool { val, .. } => self.equals_const_bool(binding_id, *val), + Constraint::ConstInt { val, ty } => self.equals_const_int(binding_id, *val, *ty), + Constraint::Variant { + ty, + variant, + fields: _, + } => self.constraint_variant(binding_id, *ty, *variant), + Constraint::Struct { ty, fields: _ } => self.constraint_struct(binding_id, *ty), + } + } + + fn constraint_some(&mut self, binding_id: BindingId) -> Result { + // Constrained binding should be an option. + let opt = self.binding_value[&binding_id] + .as_option() + .expect("target of some constraint should be an option") + .clone(); + + // Constraint: option is Some. + Ok(opt.some) + } + + fn constraint_variant( + &mut self, + binding_id: BindingId, + ty: TypeId, + variant: VariantId, + ) -> Result { + // Constrained binding should be an enum. + let e = self.binding_value[&binding_id] + .as_enum() + .ok_or(self.error("target of variant constraint should be an enum"))? + .clone(); + + // TODO(mbm): check the enum type is correct? + + // Lookup variant. + let variant_type = self.prog.tyenv.get_variant(ty, variant); + let variant_name = self.prog.tyenv.syms[variant_type.name.index()].as_str(); + + // Assumption: discriminant equals variant. + let variant = e.try_variant_by_name(variant_name)?; + let discriminator = self.discriminator(&e, variant); + Ok(discriminator) + } + + fn constraint_struct(&mut self, binding_id: BindingId, _ty: TypeId) -> Result { + // Target binding should be a struct. + self.binding_value[&binding_id] + .as_struct() + .ok_or(self.error("target of struct constraint should be a struct"))?; + + // A struct constraint is irrefutable: a value of the given struct type + // always matches. + Ok(self.boolean(true)) + } + + fn spec_expr(&mut self, expr: &spec::Expr, vars: &Variables) -> Result { + dbg_depth!("spec_expr"); + self.position_stack.push(expr.pos); + let result = self.spec_expr_kind(&expr.x, vars); + self.position_stack.pop(); + result + } + + fn spec_expr_kind(&mut self, expr: &spec::ExprKind, vars: &Variables) -> Result { + macro_rules! unary_expr { + ($expr:path, $x:ident) => {{ + let $x = self.spec_expr($x, vars)?; + Ok(self.scalar($expr(self.as_scalar($x)?))) + }}; + } + + macro_rules! binary_expr { + ($expr:path, $x:ident, $y:ident) => {{ + let $x = self.spec_expr($x, vars)?; + let $y = self.spec_expr($y, vars)?; + Ok(self.scalar($expr(self.as_scalar($x)?, self.as_scalar($y)?))) + }}; + } + + macro_rules! variadic_expr { + ($expr:path, $xs:ident) => {{ + let exprs: Vec = $xs + .iter() + .map(|x| { + let x = self.spec_expr(x, vars)?; + self.as_scalar(x) + }) + .collect::>>()?; + Ok(Symbolic::Scalar( + exprs + .into_iter() + .rev() + .reduce(|acc, e| self.dedup_expr($expr(e, acc))) + .ok_or(self.error("empty variadic expression"))?, + )) + }}; + } + + match expr { + spec::ExprKind::Var(v) => { + let v = vars.expect(&v.0)?; + Ok(v.clone()) + } + + spec::ExprKind::Const(c) => Ok(self.constant(c.clone()).into()), + + spec::ExprKind::Constructor(constructor) => self.construct(constructor, vars), + + spec::ExprKind::Field(name, x) => { + let x = self.spec_expr(x, vars)?; + self.spec_field(name, x) + } + + spec::ExprKind::Discriminator(variant, x) => { + let x = self.spec_expr(x, vars)?; + self.spec_discriminator(variant, x) + } + + spec::ExprKind::Not(x) => unary_expr!(Expr::Not, x), + spec::ExprKind::And(xs) => variadic_expr!(Expr::And, xs), + spec::ExprKind::Or(xs) => variadic_expr!(Expr::Or, xs), + spec::ExprKind::Imp(x, y) => binary_expr!(Expr::Imp, x, y), + + spec::ExprKind::Eq(x, y) => { + let x = self.spec_expr(x, vars)?; + let y = self.spec_expr(y, vars)?; + Ok(self.values_equal(x, y)?.into()) + } + + spec::ExprKind::Lt(x, y) => binary_expr!(Expr::Lt, x, y), + spec::ExprKind::Lte(x, y) => binary_expr!(Expr::Lte, x, y), + spec::ExprKind::Gt(x, y) => binary_expr!(Expr::Lt, y, x), + spec::ExprKind::Gte(x, y) => binary_expr!(Expr::Lte, y, x), + spec::ExprKind::BVUlt(x, y) => binary_expr!(Expr::BVUlt, x, y), + spec::ExprKind::BVUle(x, y) => binary_expr!(Expr::BVUle, x, y), + spec::ExprKind::BVSge(x, y) => binary_expr!(Expr::BVSge, x, y), + spec::ExprKind::BVSlt(x, y) => binary_expr!(Expr::BVSlt, x, y), + spec::ExprKind::BVSle(x, y) => binary_expr!(Expr::BVSle, x, y), + spec::ExprKind::BVSgt(x, y) => binary_expr!(Expr::BVSgt, x, y), + spec::ExprKind::BVUgt(x, y) => binary_expr!(Expr::BVUgt, x, y), + spec::ExprKind::BVUge(x, y) => binary_expr!(Expr::BVUge, x, y), + spec::ExprKind::BVSaddo(x, y) => binary_expr!(Expr::BVSaddo, x, y), + spec::ExprKind::BVNot(x) => unary_expr!(Expr::BVNot, x), + spec::ExprKind::BVNeg(x) => unary_expr!(Expr::BVNeg, x), + spec::ExprKind::Cls(x) => unary_expr!(Expr::Cls, x), + spec::ExprKind::Clz(x) => unary_expr!(Expr::Clz, x), + spec::ExprKind::Rev(x) => unary_expr!(Expr::Rev, x), + spec::ExprKind::Popcnt(x) => unary_expr!(Expr::Popcnt, x), + spec::ExprKind::Add(x, y) => binary_expr!(Expr::Add, x, y), + spec::ExprKind::Sub(x, y) => binary_expr!(Expr::Sub, x, y), + spec::ExprKind::Mul(x, y) => binary_expr!(Expr::Mul, x, y), + spec::ExprKind::BVAdd(x, y) => binary_expr!(Expr::BVAdd, x, y), + spec::ExprKind::BVSub(x, y) => binary_expr!(Expr::BVSub, x, y), + spec::ExprKind::BVMul(x, y) => binary_expr!(Expr::BVMul, x, y), + spec::ExprKind::BVSDiv(x, y) => binary_expr!(Expr::BVSDiv, x, y), + spec::ExprKind::BVAnd(x, y) => binary_expr!(Expr::BVAnd, x, y), + spec::ExprKind::BVOr(x, y) => binary_expr!(Expr::BVOr, x, y), + spec::ExprKind::BVXor(x, y) => binary_expr!(Expr::BVXor, x, y), + spec::ExprKind::BVShl(x, y) => binary_expr!(Expr::BVShl, x, y), + spec::ExprKind::BVLShr(x, y) => binary_expr!(Expr::BVLShr, x, y), + spec::ExprKind::BVAShr(x, y) => binary_expr!(Expr::BVAShr, x, y), + spec::ExprKind::BVUDiv(x, y) => binary_expr!(Expr::BVUDiv, x, y), + spec::ExprKind::BVURem(x, y) => binary_expr!(Expr::BVURem, x, y), + spec::ExprKind::BVSRem(x, y) => binary_expr!(Expr::BVSRem, x, y), + spec::ExprKind::BVRotl(x, y) => binary_expr!(Expr::BVRotl, x, y), + spec::ExprKind::BVRotr(x, y) => binary_expr!(Expr::BVRotr, x, y), + + spec::ExprKind::Conditional(c, t, e) => { + let c = self.spec_expr(c, vars)?; + let t = self.spec_expr(t, vars)?; + let e = self.spec_expr(e, vars)?; + self.conditional(self.as_scalar(c)?, t, e) + } + + spec::ExprKind::Switch(on, arms) => self.spec_switch(on, arms, vars), + + spec::ExprKind::Match(on, arms) => self.spec_match(on, arms, vars), + + spec::ExprKind::Let(defs, body) => self.spec_let(defs, body, vars), + + spec::ExprKind::With(decls, body) => self.spec_with(decls, body, vars), + + spec::ExprKind::Expand(ident, args) => self.spec_expand(ident, args, vars), + + spec::ExprKind::BVZeroExt(w, x) => binary_expr!(Expr::BVZeroExt, w, x), + spec::ExprKind::BVSignExt(w, x) => binary_expr!(Expr::BVSignExt, w, x), + spec::ExprKind::BVConvTo(w, x) => binary_expr!(Expr::BVConvTo, w, x), + + spec::ExprKind::BVExtract(h, l, x) => { + let x = self.spec_expr(x, vars)?; + Ok(self.scalar(Expr::BVExtract(*h, *l, self.as_scalar(x)?))) + } + + spec::ExprKind::BVConcat(xs) => variadic_expr!(Expr::BVConcat, xs), + spec::ExprKind::BVReplicate(x, n) => { + let x = self.spec_expr(x, vars)?; + let r = self.replicate(self.as_scalar(x)?, *n)?; + Ok(Symbolic::Scalar(r)) + } + spec::ExprKind::Int2BV(w, x) => binary_expr!(Expr::Int2BV, w, x), + spec::ExprKind::BV2Nat(x) => unary_expr!(Expr::BV2Nat, x), + spec::ExprKind::ToFP(w, x) => binary_expr!(Expr::ToFP, w, x), + spec::ExprKind::ToFPUnsigned(w, x) => binary_expr!(Expr::ToFPUnsigned, w, x), + spec::ExprKind::ToFPFromFP(w, x) => binary_expr!(Expr::ToFPFromFP, w, x), + spec::ExprKind::FPToUBV(w, x) => binary_expr!(Expr::FPToUBV, w, x), + spec::ExprKind::FPToSBV(w, x) => binary_expr!(Expr::FPToSBV, w, x), + spec::ExprKind::WidthOf(x) => unary_expr!(Expr::WidthOf, x), + + spec::ExprKind::As(x, ty) => { + let x = self.spec_expr(x, vars)?; + self.conditions.qualifiers.push(Qualifier { + value: x.clone(), + ty: ty.clone(), + }); + Ok(x) + } + + spec::ExprKind::FPPositiveInfinity(x) => unary_expr!(Expr::FPPositiveInfinity, x), + spec::ExprKind::FPNegativeInfinity(x) => unary_expr!(Expr::FPNegativeInfinity, x), + spec::ExprKind::FPPositiveZero(x) => unary_expr!(Expr::FPPositiveZero, x), + spec::ExprKind::FPNegativeZero(x) => unary_expr!(Expr::FPNegativeZero, x), + spec::ExprKind::FPNaN(x) => unary_expr!(Expr::FPNaN, x), + spec::ExprKind::FPEq(x, y) => binary_expr!(Expr::FPEq, x, y), + spec::ExprKind::FPNe(x, y) => binary_expr!(Expr::FPNe, x, y), + spec::ExprKind::FPLt(x, y) => binary_expr!(Expr::FPLt, x, y), + spec::ExprKind::FPGt(x, y) => binary_expr!(Expr::FPGt, x, y), + spec::ExprKind::FPLe(x, y) => binary_expr!(Expr::FPLe, x, y), + spec::ExprKind::FPGe(x, y) => binary_expr!(Expr::FPGe, x, y), + spec::ExprKind::FPAdd(x, y) => binary_expr!(Expr::FPAdd, x, y), + spec::ExprKind::FPSub(x, y) => binary_expr!(Expr::FPSub, x, y), + spec::ExprKind::FPMul(x, y) => binary_expr!(Expr::FPMul, x, y), + spec::ExprKind::FPDiv(x, y) => binary_expr!(Expr::FPDiv, x, y), + spec::ExprKind::FPMin(x, y) => binary_expr!(Expr::FPMin, x, y), + spec::ExprKind::FPMax(x, y) => binary_expr!(Expr::FPMax, x, y), + spec::ExprKind::FPNeg(x) => unary_expr!(Expr::FPNeg, x), + spec::ExprKind::FPCeil(x) => unary_expr!(Expr::FPCeil, x), + spec::ExprKind::FPFloor(x) => unary_expr!(Expr::FPFloor, x), + spec::ExprKind::FPSqrt(x) => unary_expr!(Expr::FPSqrt, x), + spec::ExprKind::FPTrunc(x) => unary_expr!(Expr::FPTrunc, x), + spec::ExprKind::FPNearest(x) => unary_expr!(Expr::FPNearest, x), + spec::ExprKind::FPIsZero(x) => unary_expr!(Expr::FPIsZero, x), + spec::ExprKind::FPIsInfinite(x) => unary_expr!(Expr::FPIsInfinite, x), + spec::ExprKind::FPIsNaN(x) => unary_expr!(Expr::FPIsNaN, x), + spec::ExprKind::FPIsNegative(x) => unary_expr!(Expr::FPIsNegative, x), + spec::ExprKind::FPIsPositive(x) => unary_expr!(Expr::FPIsPositive, x), + + spec::ExprKind::Macro(params, body) => Ok(Symbolic::Macro(Macro { + params: params.clone(), + body: body.clone(), + })), + } + } + + fn spec_expr_no_vars(&mut self, expr: &spec::Expr) -> Result { + let no_vars = Variables::new(); + self.spec_expr(expr, &no_vars) + } + + fn spec_typed_value(&mut self, val: i128, ty: &Type) -> Result { + match ty { + Type::Bool => Ok(self.boolean(match val { + 0 => false, + 1 => true, + _ => bail!("boolean value must be zero or one"), + })), + Type::Int => Ok(self.constant(Const::Int(val))), + Type::BitVector(Width::Bits(w)) => { + Ok(self.constant(Const::BitVector(*w, val.try_into()?))) + } + _ => bail!("cannot construct constant of type {ty}"), + } + } + + fn construct(&mut self, constructor: &Constructor, vars: &Variables) -> Result { + dbg_depth!("construct"); + match constructor { + Constructor::Enum { + name, + variant, + args, + } => { + // Lookup ISLE type by name. + let type_id = self + .prog + .tyenv + .get_type_by_name(name) + .ok_or(self.error(format!("unknown enum type {name}", name = name.0)))?; + + // Determine type model. + let model = self + .prog + .specenv + .type_model + .get(&type_id) + .ok_or(self.error(format!( + "unspecified model for type `{name}`: this enum type is being \ + constructed here, but has no `(model ...)` declaration. Add a \ + `(model {name} (enum ...))` form in a spec file listing its variants \ + so the verifier knows its representation.", + name = name.0 + )))?; + + // Should be an enum. + let e = model.as_enum().ok_or( + self.error(format!("{name} expected to have enum type", name = name.0)), + )?; + + // Lookup variant. + let variant = + e.variants.iter().find(|v| v.name.0 == variant.0).ok_or( + self.error(format!("unknown variant {variant}", variant = variant.0)), + )?; + + // Discriminant: constant value since we are constructing a known variant. + let discriminant = self.constant(Const::Int(variant.id.index().try_into()?)); + + // Variants: undefined except for the variant under construction. + let variants = e + .variants + .iter() + .map(|v| { + // For all except the variant under construction, allocate an undefined variant. + if v.id != variant.id { + return self.alloc_variant(v, "undef".to_string()); + } + + // Construct a variant provided arguments. + assert_eq!(args.len(), v.fields.len()); + let fields = zip(&v.fields, args) + .map(|(f, a)| { + Ok(SymbolicField { + name: f.name.0.clone(), + value: self.spec_expr(a, vars)?, + }) + }) + .collect::>()?; + Ok(SymbolicVariant { + name: v.name.0.clone(), + id: v.id, + discriminant: v.id.index(), + value: Symbolic::Struct(fields), + }) + }) + .collect::>()?; + + Ok(self.new_enum(type_id, discriminant, variants)?) + } + Constructor::Struct { fields } => Ok(Symbolic::Struct( + fields + .iter() + .map(|f| { + Ok(SymbolicField { + name: f.name.0.clone(), + value: self.spec_expr(&f.value, vars)?, + }) + }) + .collect::>()?, + )), + } + } + + fn spec_field(&mut self, name: &Ident, v: Symbolic) -> Result { + log::trace!("access field {name} from {v}", name = name.0); + + let fields = v + .as_struct() + .ok_or(self.error("field access from non-struct value"))?; + + let field = fields + .iter() + .find(|f| f.name == name.0) + .ok_or(self.error(format!( + "attempt to access nonexistent struct field: {}", + name.0 + )))?; + + Ok(field.value.clone()) + } + + fn spec_discriminator(&mut self, name: &Ident, v: Symbolic) -> Result { + let e = v + .as_enum() + .ok_or(self.error("discriminator for non-enum value"))?; + let variant = e.try_variant_by_name(&name.0)?; + let discriminator = self.discriminator(e, variant); + Ok(discriminator.into()) + } + + fn discriminator(&mut self, e: &SymbolicEnum, variant: &SymbolicVariant) -> ExprId { + let discriminant = self.constant(Const::Int(variant.discriminant.try_into().unwrap())); + self.exprs_equal(e.discriminant, discriminant) + } + + fn spec_switch( + &mut self, + on: &spec::Expr, + arms: &[(spec::Expr, spec::Expr)], + vars: &Variables, + ) -> Result { + // Generate branch arms. + let on = self.spec_expr(on, vars)?; + let cases = arms + .iter() + .map(|(value, then)| { + let value = self.spec_expr(value, vars)?; + let cond = self.values_equal(on.clone(), value)?; + Ok((cond, self.spec_expr(then, vars)?)) + }) + .collect::>>()?; + + // Build an expression splitting over cases. + self.cases(&cases) + } + + fn spec_match(&mut self, on: &spec::Expr, arms: &[Arm], vars: &Variables) -> Result { + // Generate the enum value to match on. + let on = self.spec_expr(on, vars)?; + let e = on.as_enum().ok_or(self.error("match on non-enum value"))?; + + // Generate cases. + let mut cases = Vec::new(); + for arm in arms { + // Lookup the variant. + let variant = e.try_variant_by_name(&arm.variant.0)?; + + // Arm condition is that the discriminant matches the variant. + let cond = self.discriminator(e, variant); + + // Arm value is the result of the body expression, evaluated with + // the variants fields brought into scope. + let Some(fields) = variant.value.as_struct() else { + bail!("variant {name} must have struct value", name = variant.name); + }; + if arm.args.len() != fields.len() { + bail!( + "incorrect number of arguments for variant {name}", + name = variant.name + ); + } + let mut arm_vars = vars.clone(); + for (arg, field) in zip(&arm.args, fields) { + arm_vars.set(arg.0.clone(), field.value.clone())?; + } + let body = self.spec_expr(&arm.body, &arm_vars)?; + + // Add case for this match arm. + cases.push((cond, body)); + } + + // Build an expression splitting over cases. + self.cases(&cases) + } + + fn cases(&mut self, cases: &[(ExprId, Symbolic)]) -> Result { + // Build an undefined fallback value. + let Some((_, value)) = cases.last() else { + bail!("must have at least one case"); + }; + let fallback = value.scalar_map(&mut |_| self.undef_variable()); + + // Represent as nested conditionals. + cases + .iter() + .rev() + .cloned() + .try_fold(fallback, |acc, (cond, then)| { + self.conditional(cond, then, acc) + }) + } + + fn spec_let( + &mut self, + defs: &[(Ident, spec::Expr)], + body: &spec::Expr, + vars: &Variables, + ) -> Result { + // Evaluate let defs. + let mut let_vars = vars.clone(); + for (name, expr) in defs { + let expr = self.spec_expr(expr, &let_vars)?; + let_vars.set(name.0.clone(), expr)?; + } + + // Evaluate body in let-binding scope. + self.spec_expr(body, &let_vars) + } + + fn spec_with( + &mut self, + decls: &[Ident], + body: &spec::Expr, + vars: &Variables, + ) -> Result { + // Declare new variables. + let mut with_vars = vars.clone(); + for name in decls { + let expr = Symbolic::Scalar(self.alloc_variable(Type::Unknown, name.0.clone())); + with_vars.set(name.0.clone(), expr)?; + } + + // Evaluate body in new scope. + self.spec_expr(body, &with_vars) + } + + fn spec_expand( + &mut self, + name: &Ident, + args: &[spec::Expr], + vars: &Variables, + ) -> Result { + // Lookup macro. + // + // Could be an inline macro in a local variable, or a macro defined at global scope. + let (params, body) = if let Some(v) = vars.get(&name.0) { + let Symbolic::Macro(m) = v else { + bail!("variable {name} is not a macro", name = name.0); + }; + (&m.params, &m.body) + } else { + let defn = self + .prog + .specenv + .macros + .get(&name.0) + .ok_or(self.error(format!("unknown macro {name}", name = name.0)))?; + (&defn.params, &defn.body) + }; + + // Build macro expansion scope. + let mut macro_vars = Variables::new(); + if params.len() != args.len() { + bail!( + "incorrect number of arguments for macro {name}", + name = name.0 + ); + } + for (param, arg) in zip(params, args) { + let arg = self.spec_expr(arg, vars)?; + macro_vars.set(param.0.clone(), arg)?; + } + + // Evaluate macro body. + self.spec_expr(body, ¯o_vars) + } + + fn replicate(&mut self, x: ExprId, n: usize) -> Result { + match n { + 0 => bail!("cannot replicate zero times"), + 1 => Ok(x), + _ => { + let h = n / 2; + let l = self.replicate(x, h)?; + let r = self.replicate(x, n - h)?; + Ok(self.dedup_expr(Expr::BVConcat(l, r))) + } + } + } + + fn conditional(&mut self, c: ExprId, t: Symbolic, e: Symbolic) -> Result { + Symbolic::merge(&t, &e, &mut |t, e| { + self.dedup_expr(Expr::Conditional(c, t, e)) + }) + } + + fn bindings_equal(&mut self, a: BindingId, b: BindingId) -> Result { + // TODO(mbm): can this be done without clones? + let a = self.binding_value[&a].clone(); + let b = self.binding_value[&b].clone(); + self.values_equal(a, b) + } + + fn values_equal(&mut self, a: Symbolic, b: Symbolic) -> Result { + dbg_depth!("values_equal"); + if std::mem::discriminant(&a) != std::mem::discriminant(&b) { + return Err(self.error("equality on different symbolic types")); + } + match (a, b) { + (Symbolic::Scalar(u), Symbolic::Scalar(v)) => Ok(self.exprs_equal(u, v)), + + (Symbolic::Struct(us), Symbolic::Struct(vs)) => { + // Field-wise equality. + // TODO(mbm): can we expect that structs are the same length? + assert_eq!(us.len(), vs.len(), "field length mismatch"); + let fields_eq = zip(us, vs) + .map(|(fu, fv)| { + assert_eq!(fu.name, fv.name, "field name mismatch"); + self.values_equal(fu.value, fv.value) + }) + .collect::>()?; + + // All fields must be equal. + Ok(self.all(fields_eq)) + } + + (Symbolic::Enum(u), Symbolic::Enum(v)) => { + // Discriminant equality. + let discriminants_eq = self.exprs_equal(u.discriminant, v.discriminant); + let mut equalities = vec![discriminants_eq]; + + // Variant equality conditions. + assert_eq!(u.variants.len(), v.variants.len(), "variant count mismatch"); + let variants_eq = zip(&u.variants, &v.variants) + .map(|(uv, vv)| { + assert_eq!(uv.name, vv.name, "variant name mismatch"); + let ud = self.discriminator(&u, uv); + let eq = self.values_equal(uv.value.clone(), vv.value.clone())?; + Ok(self.dedup_expr(Expr::Imp(ud, eq))) + }) + .collect::>>()?; + equalities.extend(variants_eq); + + // Combine discriminant and variant conditions. + Ok(self.all(equalities)) + } + + (Symbolic::Tuple(us), Symbolic::Tuple(vs)) => { + // Field-wise equality. + // TODO(mbm): can we expect that tuples are the same length? + assert_eq!(us.len(), vs.len(), "tuple length mismatch"); + let fields_eq = zip(us, vs) + .map(|(u, v)| self.values_equal(u, v)) + .collect::>()?; + + // All fields must be equal. + Ok(self.all(fields_eq)) + } + + ref c => todo!("values equal: {c:?}"), + } + } + + fn exprs_equal(&mut self, lhs: ExprId, rhs: ExprId) -> ExprId { + self.dedup_expr(Expr::Eq(lhs, rhs)) + } + + fn all(&mut self, exprs: Vec) -> ExprId { + exprs + .into_iter() + .reduce(|acc, e| self.dedup_expr(Expr::And(acc, e))) + .unwrap_or_else(|| self.boolean(true)) + } + + fn any(&mut self, exprs: Vec) -> ExprId { + exprs + .into_iter() + .reduce(|acc, e| self.dedup_expr(Expr::Or(acc, e))) + .unwrap_or_else(|| self.boolean(false)) + } + + fn boolean(&mut self, value: bool) -> ExprId { + self.constant(Const::Bool(value)) + } + + fn constant(&mut self, c: Const) -> ExprId { + self.dedup_expr(Expr::Const(c)) + } + + /// Determine the type of the given binding in the context of the + /// [Expansion] we are constructing verification conditions for. + fn binding_type(&self, binding: &Binding) -> BindingType { + binding_type( + binding, + self.expansion.term, + self.prog, + |binding_id: BindingId| self.expansion.bindings[binding_id.index()].clone().unwrap(), + ) + } + + fn alloc_binding(&mut self, binding_type: &BindingType, name: String) -> Result { + dbg_depth!("alloc_binding"); + match binding_type { + BindingType::Base(type_id) => self.alloc_model(*type_id, name), + BindingType::Option(inner_type) => { + let some = self.alloc_variable(Type::Bool, Variable::component_name(&name, "some")); + let inner = Box::new( + self.alloc_binding(inner_type, Variable::component_name(&name, "inner"))?, + ); + Ok(Symbolic::Option(SymbolicOption { some, inner })) + } + BindingType::Tuple(inners) => { + let inners = inners + .iter() + .enumerate() + .map(|(i, inner_type)| { + self.alloc_binding( + inner_type, + Variable::component_name(&name, &i.to_string()), + ) + }) + .collect::>()?; + Ok(Symbolic::Tuple(inners)) + } + } + } + + fn alloc_value(&mut self, ty: &Compound, name: String) -> Result { + dbg_depth!("alloc_value"); + match ty { + Compound::Primitive(ty) => Ok(Symbolic::Scalar(self.alloc_variable(ty.clone(), name))), + Compound::Struct(fields) => Ok(Symbolic::Struct( + fields + .iter() + .map(|f| { + Ok(SymbolicField { + name: f.name.0.clone(), + value: self + .alloc_value(&f.ty, Variable::component_name(&name, &f.name.0))?, + }) + }) + .collect::>()?, + )), + Compound::Enum(e) => { + let discriminant = + self.alloc_variable(Type::Int, Variable::component_name(&name, "discriminant")); + let variants = e + .variants + .iter() + .map(|v| self.alloc_variant(v, name.clone())) + .collect::>()?; + Ok(self.new_enum(e.id, discriminant, variants)?) + } + Compound::Named(_) => { + let ty = self.prog.specenv.resolve_type(ty, &self.prog.tyenv)?; + self.alloc_value(&ty, name) + } + } + } + + fn new_enum( + &mut self, + ty: TypeId, + discriminant: ExprId, + variants: Vec, + ) -> Result { + // Construct symbolic enum and ensure it's valid. + let e = SymbolicEnum { + ty, + discriminant, + variants, + }; + e.validate()?; + + // Assume discriminant invariant: positive integer less than number of + // variants. + let zero = self.constant(Const::Int(0)); + let num_variants = self.constant(Const::Int(e.variants.len().try_into()?)); + let discriminant_positive = self.dedup_expr(Expr::Lte(zero, discriminant)); + let discriminant_less_than_num_variants = + self.dedup_expr(Expr::Lt(discriminant, num_variants)); + let discriminant_in_range = self.dedup_expr(Expr::And( + discriminant_positive, + discriminant_less_than_num_variants, + )); + self.conditions.assumptions.push(discriminant_in_range); + + // Variant term instantiations. + let ret = Symbolic::Enum(e.clone()); + for variant in &e.variants { + let term = self.prog.get_variant_term(e.ty, variant.id); + let args = variant.field_values()?; + self.record_term_instantiation(term, args, ret.clone())?; + } + + Ok(ret) + } + + fn alloc_variant(&mut self, variant: &Variant, name: String) -> Result { + let name = Variable::component_name(&name, &variant.name.0); + Ok(SymbolicVariant { + name: variant.name.0.clone(), + id: variant.id, + discriminant: variant.id.index(), + value: self.alloc_value(&variant.ty(), name)?, + }) + } + + fn alloc_model(&mut self, type_id: TypeId, name: String) -> Result { + let type_name = self.prog.type_name(type_id); + let term_name = self.prog.term_name(self.expansion.term); + let ty = self + .prog + .specenv + .type_model + .get(&type_id) + .ok_or(self.error(format!( + "unspecified model for type `{type_name}`: while building verification \ + conditions for term `{term_name}`, the binding `{name}` has type `{type_name}`, \ + but that type has no `(model ...)` declaration. Add a `(model {type_name} ...)` \ + form in a spec file describing its representation (for example a bitvector of \ + some width, or an enum listing its variants) so the verifier can allocate a \ + symbolic value for it." + )))?; + self.alloc_value(ty, name) + } + + fn undef_variable(&mut self) -> ExprId { + self.alloc_variable(Type::Unknown, "undef".to_string()) + } + + fn alloc_variable(&mut self, ty: Type, name: String) -> ExprId { + let v = VariableId(self.conditions.variables.len()); + self.conditions.variables.push(Variable { ty, name }); + self.dedup_expr(Expr::Variable(v)) + } + + fn scalar(&mut self, expr: Expr) -> Symbolic { + Symbolic::Scalar(self.dedup_expr(expr)) + } + + fn as_scalar(&self, v: Symbolic) -> Result { + v.as_scalar().ok_or(self.error("expected scalar value")) + } + + fn dedup_expr(&mut self, expr: Expr) -> ExprId { + // Dedupe, if pure. + let maybe_id = if expr.pure() { + self.expr_map.get(&expr) + } else { + None + }; + + // Otherwise, allocate new one. + let id = if let Some(id) = maybe_id { + *id + } else { + let id = ExprId(self.conditions.exprs.len()); + self.conditions.exprs.push(expr.clone()); + self.expr_map.insert(expr, id); + id + }; + + if let Some(pos) = self.position_stack.last() { + self.conditions.pos.insert(id, *pos); + } + + id + } + + fn error(&self, msg: impl Into) -> Error { + if let Some(pos) = self.position_stack.last() { + self.prog.error_at_pos(*pos, msg).into() + } else { + Error::msg(msg.into()) + } + } +} diff --git a/cranelift/isle/veri/veri/tests/filetests.rs b/cranelift/isle/veri/veri/tests/filetests.rs new file mode 100644 index 000000000000..fbbe4ad0d7e7 --- /dev/null +++ b/cranelift/isle/veri/veri/tests/filetests.rs @@ -0,0 +1,27 @@ +use std::path::PathBuf; + +use cranelift_isle_veri::runner::Runner; +use cranelift_isle_veri_test_macros::file_tests; +use tempfile::tempdir; + +#[file_tests(path = "filetests/pass", ext = "isle")] +fn pass(test_file: &str) { + let inputs = vec![PathBuf::from(test_file)]; + let mut runner = Runner::from_files(&inputs).expect("should be able to create runner"); + let temp_dir = tempdir().expect("should be able to create temporary log directory"); + runner.set_log_dir(temp_dir.path().join("log")); + runner.include_first_rule_named(); + runner.set_root_term("test"); + runner.run().expect("verification should pass"); +} + +#[file_tests(path = "filetests/broken", ext = "isle")] +fn broken(test_file: &str) { + let inputs = vec![PathBuf::from(test_file)]; + let mut runner = Runner::from_files(&inputs).expect("should be able to create runner"); + let temp_dir = tempdir().expect("should be able to create temporary log directory"); + runner.set_log_dir(temp_dir.path().join("log")); + runner.include_first_rule_named(); + runner.set_root_term("test"); + runner.run().expect_err("verification should fail"); +} diff --git a/cranelift/isle/veri/veri_engine/Cargo.toml b/cranelift/isle/veri/veri_engine/Cargo.toml deleted file mode 100644 index 02ac46c38b8d..000000000000 --- a/cranelift/isle/veri/veri_engine/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "veri_engine" -license = "Apache-2.0 WITH LLVM-exception" -authors = ["Alexa VanHattum", "Monica Pardeshi", "Michael McLoughlin", "Wellesley Programming Systems Lab"] -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -name = "veri_engine_lib" -path = "src/lib.rs" - -[[bin]] -name = "crocus" -path = "src/main.rs" - -[dependencies] -cranelift-isle = { path = "../../isle" } -cranelift-codegen = { path = "../../../codegen", features = ["all-arch"] } -cranelift-codegen-meta = { path = "../../../codegen/meta" } -veri_ir = { path = "../veri_ir" } -easy-smt = "0.2.2" -clap = { workspace = true } -itertools = { workspace = true } -log = { workspace = true } -env_logger = { workspace = true } -anyhow = { workspace = true } - -[dev-dependencies] -strum = "0.24.0" -strum_macros = "0.24.0" diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_32_general_rotl_to_rotr.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_32_general_rotl_to_rotr.isle deleted file mode 100644 index b0ed53a641c2..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_32_general_rotl_to_rotr.isle +++ /dev/null @@ -1,34 +0,0 @@ - -(spec (lower arg) (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(spec (sub ty a b) - (provide (= (bvsub a b) result))) -(decl sub (Type Reg Reg) Reg) -(extern constructor sub sub) - -(spec (zero_reg) - (provide (= (zero_ext 64 #x0000000000000000) result))) -(decl zero_reg () Reg) -(extern constructor zero_reg zero_reg) - -(spec (a64_rotr ty x y) - (provide (= result - (if (= ty 32) - (zero_ext 64 (rotr (extract 31 0 x) (extract 31 0 y))) - (rotr x y)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_rotr (Type Reg Reg) Reg) -(extern constructor a64_rotr a64_rotr) - -;; BROKEN: order of arguments to sub flipped -;; General 32-bit case. -(rule (lower (has_type $I32 (rotl x y))) - (let ((amt Reg (value_regs_get y 0)) - (neg_shift Reg (sub $I32 amt (zero_reg)))) - (a64_rotr $I32 x neg_shift))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_rotl_to_rotr.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_rotl_to_rotr.isle deleted file mode 100644 index 101a5fb8b910..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_rotl_to_rotr.isle +++ /dev/null @@ -1,125 +0,0 @@ -(type ImmLogic (primitive ImmLogic)) -(type Imm12 (primitive Imm12)) - -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(spec (sub ty a b) (provide (= (bvsub a b) result))) -(decl sub (Type Reg Reg) Reg) -(extern constructor sub sub) - -(spec (zero_reg)(provide (= (zero_ext 64 #x0000000000000000) result))) -(decl zero_reg () Reg) -(extern constructor zero_reg zero_reg) - -(spec (extend a b c d) - (provide - (if b - (= result (sign_ext (bv2int d) (conv_to (bv2int c) a))) - (= result (zero_ext (bv2int d) (conv_to (bv2int c) a)))))) -(decl extend (Reg bool u8 u8) Reg) -(extern constructor extend extend) - -(spec (and_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvand (extract 31 0 x) (extract 31 0 y)))) - (64 (bvand x (zero_ext 64 y)))))) - (require - (or - (= y (bvand y #x000fff)) - (= y (bvand y #xfff000))))) -(decl and_imm (Type Reg ImmLogic) Reg) -(extern constructor and_imm and_imm) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(extern constructor put_in_reg_zext32 put_in_reg_zext32) - -;; Corresponding rust: -;; fn rotr_mask(&mut self, ty: Type) -> ImmLogic { -;; ImmLogic::maybe_from_u64((ty.bits() - 1) as u64, I32).unwrap() -;; } -;; -(spec (rotr_mask x) (provide (= (bvsub (int2bv 64 x) #x0000000000000001) result))) -(decl rotr_mask (Type) ImmLogic) -(extern constructor rotr_mask rotr_mask) - -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require - (or - (= b (bvand b #x000fff)) - (= b (bvand b #xfff000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(extern constructor sub_imm sub_imm) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -(spec (lsr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvlshr (extract 31 0 a) (extract 31 0 b)))) - (64 (bvlshr a b)))))) -(decl lsr (Type Reg Reg) Reg) -(extern constructor lsr lsr) - -(spec (lsl ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvshl (extract 31 0 a) (extract 31 0 b)))) - (64 (bvshl a b)))))) -(decl lsl (Type Reg Reg) Reg) -(extern constructor lsl lsl) - -(spec (orr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvor (extract 31 0 a) (extract 31 0 b)))) - (64 (bvor a b)))))) -(decl orr (Type Reg Reg) Reg) -(extern constructor orr orr) - -;; Instruction formats. -(type MInst - (enum -)) - -;; General 8/16-bit case. -;; BROKEN: no negation -(rule -2 (lower (has_type (fits_in_16 ty) (rotl x y))) - (let ((amt Reg (value_regs_get y 0))) - (small_rotr ty (put_in_reg_zext32 x) amt))) - - -(spec (small_rotr t x y) - (provide - (= result - (switch t - (8 (conv_to 64 (rotr (extract 7 0 x) (extract 7 0 y)))) - (16 (conv_to 64 (rotr (extract 15 0 x) (extract 15 0 y))))))) - (require - (or (= t 8) (= t 16)) - (switch t - (8 (= (extract 31 8 x) #x000000)) - (16 (= (extract 31 16 x) #x0000))))) -(decl small_rotr (Type Reg Reg) Reg) -(extern constructor small_rotr small_rotr) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle deleted file mode 100644 index fd1ddf64b5ed..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle +++ /dev/null @@ -1,190 +0,0 @@ -(type ImmLogic (primitive ImmLogic)) -(type ImmShift (primitive ImmShift)) -(type Imm12 (primitive Imm12)) - -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(spec (sub ty a b) (provide (= (bvsub a b) result))) -(decl sub (Type Reg Reg) Reg) -(extern constructor sub sub) - -(spec (zero_reg) - (provide (= (zero_ext 64 #x0000000000000000) result))) -(decl zero_reg () Reg) -(extern constructor zero_reg zero_reg) - -(spec (extend a b c d) - (provide - (if b - (= result (sign_ext (bv2int d) (conv_to (bv2int c) a))) - (= result (zero_ext (bv2int d) (conv_to (bv2int c) a)))))) -(decl extend (Reg bool u8 u8) Reg) -(extern constructor extend extend) - -(spec (and_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvand (extract 31 0 x) (extract 31 0 y)))) - (64 (bvand x (zero_ext 64 y)))))) - (require - (or - (= y (bvand y #x000fff)) - (= y (bvand y #xfff000))))) -(decl and_imm (Type Reg ImmLogic) Reg) -(extern constructor and_imm and_imm) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(extern constructor put_in_reg_zext32 put_in_reg_zext32) - -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require - (or - (= b (bvand b #x000fff)) - (= b (bvand b #xfff000))))) -(decl sub_imm (Type Reg Imm12) Reg) -(extern constructor sub_imm sub_imm) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -(spec (lsr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvlshr (extract 31 0 a) (extract 31 0 b)))) - (64 (bvlshr a b)))))) -(decl lsr (Type Reg Reg) Reg) -(extern constructor lsr lsr) - -(spec (lsl ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvshl (extract 31 0 a) (extract 31 0 b)))) - (64 (bvshl a b)))))) -(decl lsl (Type Reg Reg) Reg) -(extern constructor lsl lsl) - -(spec (orr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvor (extract 31 0 a) (extract 31 0 b)))) - (64 (bvor a b)))))) -(decl orr (Type Reg Reg) Reg) -(extern constructor orr orr) - - -;; fn imm_shift_from_imm64(&mut self, ty: Type, val: Imm64) -> Option { -;; let imm_value = (val.bits() as u64) & ((ty.bits() - 1) as u64); -;; ImmShift::maybe_from_u64(imm_value) -;; } - -;; Add an assertion that the value fits, using the constant with 56 1's then 6 0's (~63) -(spec (imm_shift_from_imm64 ty x) - (provide (= result (extract 5 0 (bvand x (bvsub (int2bv 64 ty) #x0000000000000001))))) - (require (bvult (bvand x (bvsub (int2bv 64 ty) #x0000000000000001)) #x0000000000000040))) -(decl pure imm_shift_from_imm64 (Type Imm64) ImmShift) -(extern constructor imm_shift_from_imm64 imm_shift_from_imm64) - -;; fn negate_imm_shift(&mut self, ty: Type, mut imm: ImmShift) -> ImmShift { -;; let size = u8::try_from(ty.bits()).unwrap(); -;; imm.imm = size.wrapping_sub(imm.value()); -;; imm.imm &= size - 1; -;; imm -;; } - -(spec (negate_imm_shift ty x) - (provide - (= result (bvand (bvsub (int2bv 6 ty) x) (bvsub (int2bv 6 ty) #b000001))))) -(decl negate_imm_shift (Type ImmShift) ImmShift) -(extern constructor negate_imm_shift negate_imm_shift) - -;; Helper for generating `lsr` instructions. -(spec (lsr_imm ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvlshr (extract 31 0 a) (zero_ext 32 b)))) - (64 (bvlshr a (zero_ext 64 b))))))) -(decl lsr_imm (Type Reg ImmShift) Reg) -(extern constructor lsr_imm lsr_imm) -(extern extractor lsr_imm lsr_imm) - -(spec (lsl_imm ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvshl (extract 31 0 a) (zero_ext 32 b)))) - (64 (bvshl a (zero_ext 64 b))))))) -(decl lsl_imm (Type Reg ImmShift) Reg) -(extern constructor lsl_imm lsl_imm) -(extern extractor lsl_imm lsl_imm) - -;; fn rotr_opposite_amount(&mut self, ty: Type, val: ImmShift) -> ImmShift { -;; let amount = val.value() & u8::try_from(ty.bits() - 1).unwrap(); -;; ImmShift::maybe_from_u64(u64::from(ty.bits()) - u64::from(amount)).unwrap() -;; } - -(spec (rotr_opposite_amount ty x) - (provide - (= (bvsub (int2bv 6 ty) (bvand x (bvsub (int2bv 6 ty) #b000001))) result))) -(decl rotr_opposite_amount (Type ImmShift) ImmShift) -(extern constructor rotr_opposite_amount rotr_opposite_amount) - -;; Instruction formats. -(type MInst - (enum -)) - -;; Specialization for the 8/16-bit case when the rotation amount is an immediate. -;; BROKEN: n is not negated in RHS -(rule -1 (lower (has_type (fits_in_16 ty) (rotl x (iconst k)))) - (if-let n (imm_shift_from_imm64 ty k)) - (small_rotr_imm ty (put_in_reg_zext32 x) n)) - -(spec (small_rotr_imm t x y) - (provide - (= result - (switch t - (8 (conv_to 64 (rotr (extract 7 0 x) (zero_ext 8 y)))) - (16 (conv_to 64 (rotr (extract 15 0 x) (zero_ext 16 y))))))) - (require - (or (= t 8) (= t 16)) - (switch t - (8 (= (extract 31 8 x) #x000000)) - (16 (= (extract 31 16 x) #x0000))) - (bvult y (int2bv 6 t)))) -(instantiate small_rotr_imm - ((args Int (bv 64) (bv 6)) (ret (bv 64)) (canon (bv 64)))) -(decl small_rotr_imm (Type Reg ImmShift) Reg) - -;; For a constant amount, we can instead do: -;; -;; rotr rd, val, #amt -;; -;; => -;; -;; lsr val_rshift, val, # -;; lsl val_lshift, val, -;; orr rd, val_lshift, val_rshift -(rule (small_rotr_imm ty val amt) - (let ((val_rshift Reg (lsr_imm $I32 val amt)) - (val_lshift Reg (lsl_imm $I32 val (rotr_opposite_amount ty amt)))) - (orr $I32 val_lshift val_rshift))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_32_band.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_32_band.isle deleted file mode 100644 index 82f74dd76358..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_32_band.isle +++ /dev/null @@ -1,80 +0,0 @@ - -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndS) - (AndNot) - ;; XOR (AArch64 calls this "EOR") - (Eor) - ;; XNOR (AArch64 calls this "EOR-NOT") - (EorNot) - ;; Add, setting flags - (AddS) - ;; Sub setting flags - (SubS) - ;; Signed multiplyhigh-word result - (SMulH) - ;; Unsigned multiplyhigh-word result - (UMulH) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl) - ;; Add with carry - (Adc) - ;; Add with carrysettings flags - (AdcS) - ;; Subtract with carry - (Sbc) - ;; Subtract with carrysettings flags - (SbcS) -)) - -;; BROKEN: swapped AND and OR -(spec (alu_rs_imm_logic_commutative op t a b) - (provide - (= result - (conv_to 64 - (switch op - ((ALUOp.And) (bvor a b)) - ((ALUOp.Orr) (bvand a b)) - ((ALUOp.Eor) (bvxor a b))))))) -(decl alu_rs_imm_logic_commutative (ALUOp Type Value Value) Reg) -(extern constructor alu_rs_imm_logic_commutative alu_rs_imm_logic_commutative) - -(rule -1 (lower (has_type (fits_in_32 ty) (band x y))) - (alu_rs_imm_logic_commutative (ALUOp.And) ty x y)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_32_bor.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_32_bor.isle deleted file mode 100644 index d3113c90509e..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_32_bor.isle +++ /dev/null @@ -1,80 +0,0 @@ - -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndS) - (AndNot) - ;; XOR (AArch64 calls this "EOR") - (Eor) - ;; XNOR (AArch64 calls this "EOR-NOT") - (EorNot) - ;; Add, setting flags - (AddS) - ;; Sub setting flags - (SubS) - ;; Signed multiplyhigh-word result - (SMulH) - ;; Unsigned multiplyhigh-word result - (UMulH) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl) - ;; Add with carry - (Adc) - ;; Add with carrysettings flags - (AdcS) - ;; Subtract with carry - (Sbc) - ;; Subtract with carrysettings flags - (SbcS) -)) - -;; BROKEN: swapped AND and OR -(spec (alu_rs_imm_logic_commutative op t a b) - (provide - (= result - (conv_to 64 - (switch op - ((ALUOp.And) (bvor a b)) - ((ALUOp.Orr) (bvand a b)) - ((ALUOp.Eor) (bvxor a b))))))) -(decl alu_rs_imm_logic_commutative (ALUOp Type Value Value) Reg) -(extern constructor alu_rs_imm_logic_commutative alu_rs_imm_logic_commutative) - -(rule -1 (lower (has_type (fits_in_32 ty) (bor x y))) - (alu_rs_imm_logic_commutative (ALUOp.Orr) ty x y)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_imul.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_imul.isle deleted file mode 100644 index afb301f8ef9c..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_imul.isle +++ /dev/null @@ -1,59 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum - ;; An ALU operation with two register sources and a register destination. - (AluRRR - (alu_op ALUOp) - (size OperandSize) -))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) -)) - -(type ALUOp3 - (enum - ;; Multiply-add - (MAdd) -)) - -(type OperandSize extern - (enum Size32 - Size64)) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -(decl alu_rrrr (ALUOp3 Type Reg Reg Reg) Reg) -(extern constructor alu_rrrr alu_rrrr) - -(spec (zero_reg) - (provide (= (zero_ext 64 #x0000000000000000) result))) -(decl zero_reg () Reg) -(extern constructor zero_reg zero_reg) - -;; Helper for calculating the `OperandSize` corresponding to a type -(decl operand_size (Type) OperandSize) -(rule (operand_size (fits_in_32 _ty)) (OperandSize.Size32)) -(rule (operand_size (fits_in_64 _ty)) (OperandSize.Size64)) - -;; Helpers for generating `madd` instructions. -(spec (madd ty a b c) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b)))) - (bvadd c (bvmul a b)))))) -(decl madd (Type Reg Reg Reg) Reg) -(rule (madd ty x y z) (alu_rrrr (ALUOp3.MAdd) ty x y z)) - -;; `i64` and smaller. -(rule (lower (has_type (fits_in_64 ty) (imul x y))) - (madd ty y y (zero_reg))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_mask_small_rotr.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_mask_small_rotr.isle deleted file mode 100644 index 7a7e132d9aa7..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_mask_small_rotr.isle +++ /dev/null @@ -1,137 +0,0 @@ -(type ImmLogic (primitive ImmLogic)) -(type Imm12 (primitive Imm12)) - -(spec (sub ty a b) (provide (= (bvsub a b) result))) -(decl sub (Type Reg Reg) Reg) -(extern constructor sub sub) - -(spec (zero_reg) - (provide (= (zero_ext 64 #x0000000000000000) result))) -(decl zero_reg () Reg) -(extern constructor zero_reg zero_reg) - -(spec (extend a b c d) - (provide - (if b - (= result (sign_ext (bv2int d) (conv_to (bv2int c) a))) - (= result (zero_ext (bv2int d) (conv_to (bv2int c) a)))))) -(decl extend (Reg bool u8 u8) Reg) -(extern constructor extend extend) - -(spec (and_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvand (extract 31 0 x) (extract 31 0 y)))) - (64 (bvand x (zero_ext 64 y)))))) - (require - (or - (= y (bvand y #x0000000000000fff)) - (= y (bvand y #x0000000000fff000))))) -(decl and_imm (Type Reg ImmLogic) Reg) -(extern constructor and_imm and_imm) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(extern constructor put_in_reg_zext32 put_in_reg_zext32) - -;; BROKEN: subtracts 2 instead of 1 -;; Corresponding rust: -;; fn rotr_mask(&mut self, ty: Type) -> ImmLogic { -;; ImmLogic::maybe_from_u64((ty.bits() - 1) as u64, I32).unwrap() -;; } -;; -(spec (rotr_mask x) (provide (= (bvsub (int2bv 64 x) #x0000000000000002) result))) -(decl rotr_mask (Type) ImmLogic) -(extern constructor rotr_mask rotr_mask) - -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(extern constructor sub_imm sub_imm) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -(spec (lsr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvlshr (extract 31 0 a) (extract 31 0 b)))) - (64 (bvlshr a b)))))) -(decl lsr (Type Reg Reg) Reg) -(extern constructor lsr lsr) - -(spec (lsl ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvshl (extract 31 0 a) (extract 31 0 b)))) - (64 (bvshl a b)))))) -(decl lsl (Type Reg Reg) Reg) -(extern constructor lsl lsl) - -(spec (orr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvor (extract 31 0 a) (extract 31 0 b)))) - (64 (bvor a b)))))) -(decl orr (Type Reg Reg) Reg) -(extern constructor orr orr) - -;; Instruction formats. -(type MInst - (enum -)) - - -(spec (small_rotr t x y) - (provide - (= result - (switch t - (8 (conv_to 64 (rotr (extract 7 0 x) (extract 7 0 y)))) - (16 (conv_to 64 (rotr (extract 15 0 x) (extract 15 0 y))))))) - (require - (or (= t 8) (= t 16)) - (switch t - (8 (= (extract 31 8 x) #x000000)) - (16 (= (extract 31 16 x) #x0000))))) -(instantiate small_rotr - ((args Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64)))) -(decl small_rotr (Type Reg Reg) Reg) - -;; For a < 32-bit rotate-right, we synthesize this as: -;; -;; rotr rd, val, amt -;; -;; => -;; -;; and masked_amt, amt, -;; sub tmp_sub masked_amt, -;; sub neg_amt, zero, tmp_sub ; neg -;; lsr val_rshift, val, masked_amt -;; lsl val_lshift, val, neg_amt -;; orr rd, val_lshift val_rshift -(rule (small_rotr ty val amt) - (let ((masked_amt Reg (and_imm $I32 amt (rotr_mask ty))) - (tmp_sub Reg (sub_imm $I32 masked_amt (u8_into_imm12 (ty_bits ty)))) - (neg_amt Reg (sub $I32 (zero_reg) tmp_sub)) - (val_rshift Reg (lsr $I32 val masked_amt)) - (val_lshift Reg (lsl $I32 val neg_amt))) - (orr $I32 val_lshift val_rshift))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_rule_or_small_rotr.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_rule_or_small_rotr.isle deleted file mode 100644 index 3ee6714f3c22..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_rule_or_small_rotr.isle +++ /dev/null @@ -1,149 +0,0 @@ -(type ImmLogic (primitive ImmLogic)) -(type Imm12 (primitive Imm12)) - -(spec (sub ty a b) (provide (= (bvsub a b) result))) -(decl sub (Type Reg Reg) Reg) -(extern constructor sub sub) - -(spec (zero_reg) - (provide (= (zero_ext 64 #x0000000000000000) result))) -(decl zero_reg () Reg) -(extern constructor zero_reg zero_reg) - -(spec (extend a b c d) - (provide - (if b - (= result (sign_ext (bv2int d) (conv_to (bv2int c) a))) - (= result (zero_ext (bv2int d) (conv_to (bv2int c) a)))))) -(decl extend (Reg bool u8 u8) Reg) -(extern constructor extend extend) - -(spec (and_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvand (extract 31 0 x) (extract 31 0 y)))) - (64 (bvand x (zero_ext 64 y)))))) - (require - (or (<= (bv2int y) 4094) - (and (<= (bv2int y) 16773119) - (= (extract 2 0 y) #b000))))) -(decl and_imm (Type Reg ImmLogic) Reg) -(extern constructor and_imm and_imm) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(extern constructor put_in_reg_zext32 put_in_reg_zext32) - -;; Corresponding rust: -;; fn rotr_mask(&mut self, ty: Type) -> ImmLogic { -;; ImmLogic::maybe_from_u64((ty.bits() - 1) as u64, I32).unwrap() -;; } -;; -(spec (rotr_mask x) (provide (= (bvsub (int2bv 64 x) #x0000000000000001) result))) -(decl rotr_mask (Type) ImmLogic) -(extern constructor rotr_mask rotr_mask) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(extern constructor sub_imm sub_imm) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -(spec (lsr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvlshr (extract 31 0 a) (extract 31 0 b)))) - (64 (bvlshr a b)))))) -(decl lsr (Type Reg Reg) Reg) -(extern constructor lsr lsr) - -(spec (lsl ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvshl (extract 31 0 a) (extract 31 0 b)))) - (64 (bvshl a b)))))) -(decl lsl (Type Reg Reg) Reg) -(extern constructor lsl lsl) - -(spec (orr ty a b) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvor (extract 31 0 a) (extract 31 0 b)))) - (64 (bvor a b)))))) -(decl orr (Type Reg Reg) Reg) -(extern constructor orr orr) - -;; Instruction formats. -(type MInst - (enum -)) - - -(spec (small_rotr t x y) - (provide - (= result - (switch t - (8 (conv_to 64 (rotr (extract 7 0 x) (extract 7 0 y)))) - (16 (conv_to 64 (rotr (extract 15 0 x) (extract 15 0 y))))))) - (require - (or (= t 8) (= t 16)) - (switch t - (8 (= (extract 31 8 x) #x000000)) - (16 (= (extract 31 16 x) #x0000))))) -(instantiate small_rotr - ((args Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64)))) -(decl small_rotr (Type Reg Reg) Reg) - -(spec (and_reg ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvand (extract 31 0 a) (extract 31 0 b))) - (bvand a b)))) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) -(decl and_reg (Type Reg Reg) Reg) -(extern constructor and_reg and_reg) - -;; BROKEN: uses AND instead of OR - -;; For a < 32-bit rotate-right, we synthesize this as: -;; -;; rotr rd, val, amt -;; -;; => -;; -;; and masked_amt, amt, -;; sub tmp_sub masked_amt, -;; sub neg_amt, zero, tmp_sub ; neg -;; lsr val_rshift, val, masked_amt -;; lsl val_lshift, val, neg_amt -;; orr rd, val_lshift val_rshift -(rule (small_rotr ty val amt) - (let ((masked_amt Reg (and_imm $I32 amt (rotr_mask ty))) - (tmp_sub Reg (sub_imm $I32 masked_amt (u8_into_imm12 (ty_bits ty)))) - (neg_amt Reg (sub $I32 (zero_reg) tmp_sub)) - (val_rshift Reg (lsr $I32 val masked_amt)) - (val_lshift Reg (lsl $I32 val neg_amt))) - (and_reg $I32 val_lshift val_rshift))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_uextend.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_uextend.isle deleted file mode 100644 index 078911f4ff99..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_uextend.isle +++ /dev/null @@ -1,33 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum - ;; A sign- or zero-extend operation. - (Extend - (rd WritableReg) - (rn Reg) - (signed bool) - (from_bits u8) - (to_bits u8)) -)) - -;; Helper for emitting `MInst.Extend` instructions. -;; BROKEN: zero_ext and sign_ext swapped -(spec (extend a b c d) - (provide - (if b - (= result (zero_ext (bv2int d) (conv_to (bv2int c) a))) - (= result (sign_ext (bv2int d) (conv_to (bv2int c) a)))))) -(decl extend (Reg bool u8 u8) Reg) -(rule (extend rn signed from_bits to_bits) - (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.Extend dst rn signed from_bits to_bits)))) - dst)) - -;; General rule for extending input to an output which fits in a single -;; register. -(rule (lower (has_type (fits_in_64 out) (uextend x @ (value_type in)))) - (extend x false (ty_bits in) (ty_bits out))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls.isle b/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls.isle deleted file mode 100644 index c238ad03bd4e..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls.isle +++ /dev/null @@ -1,30 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) -)) - -(type BitOp - (enum - (Cls) -)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(spec (a64_cls ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (cls (extract 31 0 a))) - (cls a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_cls (Type Reg) Reg) -(rule (a64_cls ty x) (bit_rr (BitOp.Cls) ty x)) - -(rule (lower (has_type ty (cls x))) - (a64_cls ty x)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls16.isle b/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls16.isle deleted file mode 100644 index 06bc46cf8cde..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls16.isle +++ /dev/null @@ -1,86 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) - (Extend - (rd WritableReg) - (rn Reg) - (signed bool) - (from_bits u8) - (to_bits u8)) -)) - -(type ALUOp - (enum - (Sub) -)) - -(type BitOp - (enum - (Cls) -)) - -(type Imm12 (primitive Imm12)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(spec (a64_cls ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (cls (extract 31 0 a))) - (cls a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_cls (Type Reg) Reg) -(rule (a64_cls ty x) (bit_rr (BitOp.Cls) ty x)) - -(decl extend (Reg bool u8 u8) Reg) -(rule (extend rn signed from_bits to_bits) - (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.Extend dst rn signed from_bits to_bits)))) - dst)) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(rule (put_in_reg_zext32 val @ (value_type (fits_in_32 ty))) - (extend val false (ty_bits ty) 32)) -(rule (put_in_reg_zext32 val @ (value_type $I32)) val) -(rule (put_in_reg_zext32 val @ (value_type $I64)) val) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -;; A reproduction of the previously reported bug: -;; https://github.com/bytecodealliance/wasmtime/issues/3248. -;; This rule should sign extend instead of zero extending. -(rule (lower (has_type $I16 (cls x))) - (sub_imm $I32 (a64_cls $I32 (put_in_reg_zext32 x)) (u8_into_imm12 16))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls8.isle b/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls8.isle deleted file mode 100644 index 78827bd5bebe..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/cls/broken_cls8.isle +++ /dev/null @@ -1,86 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) - (Extend - (rd WritableReg) - (rn Reg) - (signed bool) - (from_bits u8) - (to_bits u8)) -)) - -(type ALUOp - (enum - (Sub) -)) - -(type BitOp - (enum - (Cls) -)) - -(type Imm12 (primitive Imm12)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(spec (a64_cls ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (cls (extract 31 0 a))) - (cls a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_cls (Type Reg) Reg) -(rule (a64_cls ty x) (bit_rr (BitOp.Cls) ty x)) - -(decl extend (Reg bool u8 u8) Reg) -(rule (extend rn signed from_bits to_bits) - (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.Extend dst rn signed from_bits to_bits)))) - dst)) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(rule (put_in_reg_zext32 val @ (value_type (fits_in_32 ty))) - (extend val false (ty_bits ty) 32)) -(rule (put_in_reg_zext32 val @ (value_type $I32)) val) -(rule (put_in_reg_zext32 val @ (value_type $I64)) val) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -;; A reproduction of the previously reported bug: -;; https://github.com/bytecodealliance/wasmtime/issues/3248. -;; This rule should sign extend instead of zero extending. -(rule (lower (has_type $I8 (cls x))) - (sub_imm $I32 (a64_cls $I32 (put_in_reg_zext32 x)) (u8_into_imm12 24))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz.isle b/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz.isle deleted file mode 100644 index b6de2c2aef03..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz.isle +++ /dev/null @@ -1,28 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) -)) - -(type BitOp - (enum - (Clz) -)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -;; Broken: no distinction on ty -(spec (a64_clz ty a) - (provide - (= result (clz a))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_clz (Type Reg) Reg) -(rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x)) - -(rule (lower (has_type ty (clz x))) - (a64_clz ty x)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz16.isle b/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz16.isle deleted file mode 100644 index 50526db53f27..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz16.isle +++ /dev/null @@ -1,83 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) - (Extend - (rd WritableReg) - (rn Reg) - (signed bool) - (from_bits u8) - (to_bits u8)) -)) - -(type ALUOp - (enum - (Sub) -)) - -(type BitOp - (enum - (Clz) -)) - -(type Imm12 (primitive Imm12)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(spec (a64_clz ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (clz (extract 31 0 a))) - (clz a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_clz (Type Reg) Reg) -(rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x)) - -(decl extend (Reg bool u8 u8) Reg) -(rule (extend rn signed from_bits to_bits) - (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.Extend dst rn signed from_bits to_bits)))) - dst)) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(rule (put_in_reg_zext32 val @ (value_type (fits_in_32 ty))) - (extend val false (ty_bits ty) 32)) -(rule (put_in_reg_zext32 val @ (value_type $I32)) val) -(rule (put_in_reg_zext32 val @ (value_type $I64)) val) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -(rule (lower (has_type $I16 (clz x))) - (sub_imm $I32 (a64_clz $I32 (put_in_reg_zext32 x)) (u8_into_imm12 15))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz8.isle b/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz8.isle deleted file mode 100644 index 7beff8c726f8..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/clz/broken_clz8.isle +++ /dev/null @@ -1,84 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) - (Extend - (rd WritableReg) - (rn Reg) - (signed bool) - (from_bits u8) - (to_bits u8)) -)) - -(type ALUOp - (enum - (Sub) -)) - -(type BitOp - (enum - (Clz) -)) - -(type Imm12 (primitive Imm12)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(spec (a64_clz ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (clz (extract 31 0 a))) - (clz a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_clz (Type Reg) Reg) -(rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x)) - -(decl extend (Reg bool u8 u8) Reg) -(rule (extend rn signed from_bits to_bits) - (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.Extend dst rn signed from_bits to_bits)))) - dst)) - -;; BROKEN: swapped order in comparison -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (> (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(rule (put_in_reg_zext32 val @ (value_type (fits_in_32 ty))) - (extend val false (ty_bits ty) 32)) -(rule (put_in_reg_zext32 val @ (value_type $I32)) val) -(rule (put_in_reg_zext32 val @ (value_type $I64)) val) - -(spec (u8_into_imm12 arg) (provide (= result (zero_ext 24 arg)))) -(decl u8_into_imm12 (u8) Imm12) -(extern constructor u8_into_imm12 u8_into_imm12) - -(rule (lower (has_type $I8 (clz x))) - (sub_imm $I32 (a64_clz $I32 (put_in_reg_zext32 x)) (u8_into_imm12 24))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz.isle b/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz.isle deleted file mode 100644 index b5f4ec3e2156..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz.isle +++ /dev/null @@ -1,42 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) -)) - -(type BitOp - (enum - (Clz) - (RBit) -)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(spec (a64_clz ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (clz (extract 31 0 a))) - (clz a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_clz (Type Reg) Reg) -(rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x)) - -(spec (rbit ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (rev (extract 31 0 a))) - (rev a)))) - (require (or (= ty 32) (= ty 64)))) -(decl rbit (Type Reg) Reg) -(rule (rbit ty x) (bit_rr (BitOp.RBit) ty x)) - -;; Broken: starts with clz instead of ctz -(rule -1 (lower (has_type ty (clz x))) - (a64_clz ty (rbit ty x))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz16.isle b/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz16.isle deleted file mode 100644 index dc34bb90cf16..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz16.isle +++ /dev/null @@ -1,88 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) - (AluRRImmLogic - (alu_op ALUOp) - (size OperandSize) - (rd WritableReg) - (rn Reg) - (imml ImmLogic)) -)) - -(type ALUOp - (enum - (Orr) -)) - -(type BitOp - (enum - (Clz) - (RBit) -)) - -(type ImmLogic (primitive ImmLogic)) - -(type OperandSize extern - (enum Size32 - Size64)) - -(decl operand_size (Type) OperandSize) -(rule (operand_size (fits_in_32 _ty)) (OperandSize.Size32)) -(rule (operand_size (fits_in_64 _ty)) (OperandSize.Size64)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(decl alu_rr_imm_logic (ALUOp Type Reg ImmLogic) Reg) -(rule (alu_rr_imm_logic op ty src imm) - (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.AluRRImmLogic op (operand_size ty) dst src imm)))) - dst)) - -(spec (a64_clz ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (clz (extract 31 0 a))) - (clz a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_clz (Type Reg) Reg) -(rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x)) - -(spec (orr_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvor (extract 31 0 x) (extract 31 0 y)))) - (64 (bvor x (zero_ext 64 y)))))) - (require - (or (<= (bv2int y) 4094) - (and (<= (bv2int y) 16773119) - (= (extract 2 0 y) #b000))))) -(decl orr_imm (Type Reg ImmLogic) Reg) -(rule (orr_imm ty x y) (alu_rr_imm_logic (ALUOp.Orr) ty x y)) - -(spec (rbit ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (rev (extract 31 0 a))) - (rev a)))) - (require (or (= ty 32) (= ty 64)))) -(decl rbit (Type Reg) Reg) -(rule (rbit ty x) (bit_rr (BitOp.RBit) ty x)) - -(spec (u64_into_imm_logic ty a) - (provide (= result a)) - (require (or (= ty 32) (= ty 64)))) -(decl u64_into_imm_logic (Type u64) ImmLogic) -(extern constructor u64_into_imm_logic u64_into_imm_logic) - -;; Broken: wrong constant -(rule (lower (has_type $I16 (ctz x))) - (a64_clz $I32 (orr_imm $I32 (rbit $I32 x) (u64_into_imm_logic $I32 0xFFFFFFFF)))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz8.isle b/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz8.isle deleted file mode 100644 index 4b99998b766b..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/ctz/broken_ctz8.isle +++ /dev/null @@ -1,87 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (BitRR - (op BitOp)) - (AluRRImmLogic - (alu_op ALUOp) - (size OperandSize) - (rd WritableReg) - (rn Reg) - (imml ImmLogic)) -)) - -(type ALUOp - (enum - (Orr) -)) - -(type BitOp - (enum - (Clz) - (RBit) -)) - -(type ImmLogic (primitive ImmLogic)) - -(type OperandSize extern - (enum Size32 - Size64)) - -(decl operand_size (Type) OperandSize) -(rule (operand_size (fits_in_32 _ty)) (OperandSize.Size32)) -(rule (operand_size (fits_in_64 _ty)) (OperandSize.Size64)) - -(decl bit_rr (BitOp Type Reg) Reg) -(extern constructor bit_rr bit_rr) - -(decl alu_rr_imm_logic (ALUOp Type Reg ImmLogic) Reg) -(rule (alu_rr_imm_logic op ty src imm) - (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.AluRRImmLogic op (operand_size ty) dst src imm)))) - dst)) - -(spec (a64_clz ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (clz (extract 31 0 a))) - (clz a)))) - (require (or (= ty 32) (= ty 64)))) -(decl a64_clz (Type Reg) Reg) -(rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x)) - -(spec (orr_imm ty x y) - (provide - (= result - (switch ty - (32 (conv_to 64 (bvor (extract 31 0 x) (extract 31 0 y)))) - (64 (bvor x (zero_ext 64 y)))))) - (require - (or (<= (bv2int y) 4094) - (and (<= (bv2int y) 16773119) - (= (extract 2 0 y) #b000))))) -(decl orr_imm (Type Reg ImmLogic) Reg) -(rule (orr_imm ty x y) (alu_rr_imm_logic (ALUOp.Orr) ty x y)) - -(spec (rbit ty a) - (provide - (= result - (if (= ty 32) - (conv_to 64 (rev (extract 31 0 a))) - (rev a)))) - (require (or (= ty 32) (= ty 64)))) -(decl rbit (Type Reg) Reg) -(rule (rbit ty x) (bit_rr (BitOp.RBit) ty x)) - -(spec (u64_into_imm_logic ty a) - (provide (= result a)) - (require (or (= ty 32) (= ty 64)))) -(decl u64_into_imm_logic (Type u64) ImmLogic) -(extern constructor u64_into_imm_logic u64_into_imm_logic) - -(rule (lower (has_type $I8 (ctz x))) - (a64_clz $I32 (orr_imm $I32 (rbit $I32 x) (u64_into_imm_logic $I32 0x80)))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_add_extend.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_add_extend.isle deleted file mode 100644 index 9b93366567f8..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_add_extend.isle +++ /dev/null @@ -1,109 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -(type ALUOp - (enum - (Add) -)) - -;; Helper type to represent a value and an extend operation fused together. -(model ExtendedValue (type (bv 67))) -(type ExtendedValue extern (enum)) - -;; We represent ExtendedValue as a bv -;; where the three most significant bits -;; encode an extend op as follows: -;; UXTB = 0b000, -;; UXTH = 0b001, -;; UXTW = 0b010, -;; UXTX = 0b011, -;; SXTB = 0b100, -;; SXTH = 0b101, -;; SXTW = 0b110, -;; SXTX = 0b111, -;; and the remaining bits encode the value. - -(model ExtendOp (enum - (UXTB #b000) - (UXTH #b001) - (UXTW #b010) - (UXTX #b011) - (SXTB #b100) - (SXTH #b101) - (SXTW #b110) - (SXTX #b111) -)) - -(type ExtendOp extern - (enum - (UXTB) - (UXTH) - (UXTW) - (UXTX) - (SXTB) - (SXTH) - (SXTW) - (SXTX) -)) - -(decl alu_rr_extend_reg (ALUOp Type Reg ExtendedValue) Reg) -(extern constructor alu_rr_extend_reg alu_rr_extend_reg) - -;; (rule (alu_rr_extend_reg op ty src1 extended_reg) -;; (let ((src2 Reg (put_extended_in_reg extended_reg)) -;; (extend ExtendOp (get_extended_op extended_reg))) -;; (alu_rrr_extend op ty src1 src2 extend))) - -;; Only including the i8 to i32 opcodes, based on the impl of extended_value_from_value -(spec (extended_value_from_value x) - (provide - (switch (extract 66 64 x) - ((ExtendOp.UXTB) (= (extract 63 0 x) (zero_ext 64 (extract 7 0 (zero_ext 64 result))))) - ((ExtendOp.UXTH) (= (extract 63 0 x) (zero_ext 64 (extract 15 0 (zero_ext 64 result))))) - ((ExtendOp.UXTW) (= (extract 63 0 x) (zero_ext 64 (extract 31 0 (zero_ext 64 result))))) - ((ExtendOp.SXTB) (= (extract 63 0 x) (sign_ext 64 (extract 7 0 (zero_ext 64 result))))) - ((ExtendOp.SXTH) (= (extract 63 0 x) (sign_ext 64 (extract 15 0 (zero_ext 64 result))))) - ((ExtendOp.SXTW) (= (extract 63 0 x) (sign_ext 64 (extract 31 0 (zero_ext 64 result))))))) - (require - (bvult (extract 66 64 x) #b110) - (not (= (extract 66 64 x) #b011)) - (= result (conv_to (widthof result) x)) - (or (= 8 (widthof result)) (= 16 (widthof result)) (= 32 (widthof result))))) -(decl extended_value_from_value (ExtendedValue) Value) -(extern extractor extended_value_from_value extended_value_from_value) - -;; BROKEN: all sign_extend with no zero_extend -(spec (add_extend ty x y) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 x) - (switch (extract 66 64 y) - ((ExtendOp.UXTB) (sign_ext 32 (extract 7 0 y))) - ((ExtendOp.UXTH) (sign_ext 32 (extract 15 0 y))) - ((ExtendOp.UXTW) (sign_ext 32 (extract 31 0 y))) - ((ExtendOp.UXTX) (sign_ext 32 (extract 31 0 y))) - ((ExtendOp.SXTB) (sign_ext 32 (extract 7 0 y))) - ((ExtendOp.SXTH) (sign_ext 32 (extract 15 0 y))) - ((ExtendOp.SXTW) (sign_ext 32 (extract 31 0 y))) - ((ExtendOp.SXTX) (sign_ext 32 (extract 31 0 y)))))) - (bvadd x - (switch (extract 66 64 y) - ((ExtendOp.UXTB) (sign_ext 64 (extract 7 0 y))) - ((ExtendOp.UXTH) (sign_ext 64 (extract 15 0 y))) - ((ExtendOp.UXTW) (sign_ext 64 (extract 31 0 y))) - ((ExtendOp.UXTX) (sign_ext 64 (extract 63 0 y))) - ((ExtendOp.SXTB) (sign_ext 64 (extract 7 0 y))) - ((ExtendOp.SXTH) (sign_ext 64 (extract 15 0 y))) - ((ExtendOp.SXTW) (sign_ext 64 (extract 31 0 y))) - ((ExtendOp.SXTX) (sign_ext 64 (extract 63 0 y))))))))) -(decl add_extend (Type Reg ExtendedValue) Reg) -(rule (add_extend ty x y) (alu_rr_extend_reg (ALUOp.Add) ty x y)) - -(rule 0 (lower (has_type (fits_in_64 ty) (iadd x (extended_value_from_value y)))) - (add_extend ty x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_base_case.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_base_case.isle deleted file mode 100644 index b3dee5138170..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_base_case.isle +++ /dev/null @@ -1,27 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -(type ALUOp - (enum - (Add) -)) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -(spec (add ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) (extract 31 0 b))) - (bvadd a b))))) -(decl add (Type Reg Reg) Reg) -(rule (add ty x y) (alu_rrr (ALUOp.Add) ty x y)) - -(rule (lower (has_type (fits_in_64 ty) (iadd x y))) - (add ty x x)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12.isle deleted file mode 100644 index 17c1186bc70a..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12.isle +++ /dev/null @@ -1,43 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Add) -)) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (add_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) (zero_ext 32 b))) - (bvadd a (zero_ext 64 b))))) - (require (or (<= (bv2int b) 4094) - (and (<= (bv2int b) 16773119) - (= (extract 2 0 b) #b000))))) -(decl add_imm (Type Reg Imm12) Reg) -(rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -;; Broken: no require -(spec (imm12_from_value arg) - (provide - (= result (conv_to (widthof result) (zero_ext 64 arg))) - (= arg (conv_to (widthof arg) (zero_ext 64 result))))) -(decl imm12_from_value (Imm12) Value) -(extern extractor imm12_from_value imm12_from_value) - -(rule (lower (has_type (fits_in_64 ty) (iadd x (imm12_from_value y)))) - (add_imm ty x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12_2.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12_2.isle deleted file mode 100644 index 4315aa1271e3..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12_2.isle +++ /dev/null @@ -1,47 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Add) -)) - -;; BROKEN: subtract instead of add -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (add_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (<= (bv2int b) 4094) - (and (<= (bv2int b) 16773119) - (= (extract 2 0 b) #b000))))) -(decl add_imm (Type Reg Imm12) Reg) -(rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_value arg) - (provide - (= result (conv_to (widthof result) (zero_ext 64 arg))) - (= arg (conv_to (widthof arg) (zero_ext 64 result)))) - (require - (or (bvult (zero_ext 64 result) #x0000000000000fff) - (and (bvult (zero_ext 64 result) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 result)) #b000))))) -(decl imm12_from_value (Imm12) Value) -(extern extractor imm12_from_value imm12_from_value) - -(rule (lower (has_type (fits_in_64 ty) (iadd (imm12_from_value x) y))) - (add_imm ty y x)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12neg.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12neg.isle deleted file mode 100644 index 7d3ea214f8cc..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12neg.isle +++ /dev/null @@ -1,52 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Sub) -)) - -;; AVH TODO: why don't more obvious errors, like sext instead of zext, break things? -;; BROKEN: * instead of sub -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvmul (extract 31 0 a) (zero_ext 32 b))) - (bvmul a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_negated_value arg) - (provide (or (bvule (bvneg (zero_ext 64 result)) #x0000000000000fff) - (and (bvule (bvneg (zero_ext 64 result)) #x0000000000fff000) - (= (extract 2 0 (bvneg (zero_ext 64 result))) #b000))) - (= result (conv_to (widthof result) (bvneg (zero_ext 64 arg)))))) -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) -(decl imm12_from_negated_value (Imm12) Value) -(extern extractor imm12_from_negated_value imm12_from_negated_value) - -(rule (lower (has_type (fits_in_64 ty) (iadd x (imm12_from_negated_value y)))) - (sub_imm ty x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12neg2.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12neg2.isle deleted file mode 100644 index 7a8a734ece94..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_imm12neg2.isle +++ /dev/null @@ -1,51 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Sub) -)) - -;; BROKEN: * instead of - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvmul (extract 31 0 a) (zero_ext 32 b))) - (bvmul a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_negated_value arg) - (provide (or (bvule (bvneg (zero_ext 64 result)) #x0000000000000fff) - (and (bvule (bvneg (zero_ext 64 result)) #x0000000000fff000) - (= (extract 2 0 (bvneg (zero_ext 64 result))) #b000))) - (= result (conv_to (widthof result) (bvneg (zero_ext 64 arg)))))) -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) -(decl imm12_from_negated_value (Imm12) Value) -(extern extractor imm12_from_negated_value imm12_from_negated_value) - -(rule (lower (has_type (fits_in_64 ty) (iadd (imm12_from_negated_value x) y))) - (sub_imm ty y x)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_madd.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_madd.isle deleted file mode 100644 index 361e694635b7..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_madd.isle +++ /dev/null @@ -1,28 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -(type ALUOp3 - (enum - ;; Multiply-add - (MAdd) -)) - -(spec (madd ty a b c) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b)))) - (bvadd c (bvmul a b)))))) -(decl madd (Type Reg Reg Reg) Reg) -(rule (madd ty x y z) (alu_rrrr (ALUOp3.MAdd) ty x y z)) - -(decl alu_rrrr (ALUOp3 Type Reg Reg Reg) Reg) -(extern constructor alu_rrrr alu_rrrr) - -(rule (lower (has_type (fits_in_64 ty) (iadd x (imul y z)))) - (madd ty x y z)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_madd2.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_madd2.isle deleted file mode 100644 index 86542aac6821..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_madd2.isle +++ /dev/null @@ -1,28 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(type ALUOp3 - (enum - (MAdd) -)) - -(spec (madd ty a b c) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b)))) - (bvadd c (bvmul a b)))))) -(decl madd (Type Reg Reg Reg) Reg) -(rule (madd ty x y z) (alu_rrrr (ALUOp3.MAdd) ty x y z)) - -(decl alu_rrrr (ALUOp3 Type Reg Reg Reg) Reg) -(extern constructor alu_rrrr alu_rrrr) - -(rule (lower (has_type (fits_in_64 ty) (iadd (imul x y) z))) - (madd ty x y x)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_msub.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_msub.isle deleted file mode 100644 index 030486e50598..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_msub.isle +++ /dev/null @@ -1,28 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(type ALUOp3 - (enum - (MSub) -)) - -(spec (msub ty a b c) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b)))) - (bvsub c (bvmul a b)))))) -(decl msub (Type Reg Reg Reg) Reg) -(rule (msub ty x y z) (alu_rrrr (ALUOp3.MSub) ty x y z)) - -(decl alu_rrrr (ALUOp3 Type Reg Reg Reg) Reg) -(extern constructor alu_rrrr alu_rrrr) - -(rule (lower (has_type (fits_in_64 ty) (isub (imul y z) x))) - (msub ty y z x)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_shift.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_shift.isle deleted file mode 100644 index f9a851e32bf8..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_shift.isle +++ /dev/null @@ -1,84 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (AluRRRShift - (shiftop ShiftOpAndAmt)) -)) - -;; ASSUMING 64 BIT MODE!!! -;; annotations will interpret this as an 10 bit field -;; the two msb encode the type of shift as follows: -;; 00: lsl -;; 01: lsr -;; 10: asr -;; 11: invalid -;; the rest will encode a 8-bit shift amount -(type ShiftOpAndAmt (primitive ShiftOpAndAmt)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -(type ALUOp (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndNot) - (Eor) - (EorNot) - (SubS) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl))) - -(decl alu_rrr_shift (ALUOp Type Reg Reg ShiftOpAndAmt) Reg) -(extern constructor alu_rrr_shift alu_rrr_shift) - -;; BROKEN: swapped shl shr -(spec (add_shift ty a b shift) - (provide - (= result (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) - (switch (extract 15 8 shift) - ((ALUOp.Lsr) (bvshl (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvlshr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvashr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))) - (bvadd a - (switch (extract 15 8 shift) - ((ALUOp.Lsr) (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvlshr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvashr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))))) -(decl add_shift (Type Reg Reg ShiftOpAndAmt) Reg) -(rule (add_shift ty x y z) (alu_rrr_shift (ALUOp.Add) ty x y z)) - -(spec (lshl_from_imm64 ty a) - (provide (= result (concat #x0e (extract 7 0 a)))) - (require (= (extract 63 8 a) #b00000000000000000000000000000000000000000000000000000000))) -(decl pure lshl_from_imm64 (Type Imm64) ShiftOpAndAmt) -(extern constructor lshl_from_imm64 lshl_from_imm64) - -(rule 7 (lower (has_type (fits_in_64 ty) - (iadd x (ishl y (iconst k))))) - (if-let amt (lshl_from_imm64 ty k)) - (add_shift ty x y amt)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_shift2.isle b/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_shift2.isle deleted file mode 100644 index 94118e758d47..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/iadd/broken_shift2.isle +++ /dev/null @@ -1,85 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (AluRRRShift - (shiftop ShiftOpAndAmt)) -)) - -;; ASSUMING 64 BIT MODE!!! -;; annotations will interpret this as an 10 bit field -;; the two msb encode the type of shift as follows: -;; 00: lsl -;; 01: lsr -;; 10: asr -;; 11: invalid -;; the rest will encode a 8-bit shift amount -(type ShiftOpAndAmt (primitive ShiftOpAndAmt)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -(type ALUOp (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndNot) - (Eor) - (EorNot) - (SubS) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl))) - -(decl alu_rrr_shift (ALUOp Type Reg Reg ShiftOpAndAmt) Reg) -(extern constructor alu_rrr_shift alu_rrr_shift) - -(spec (add_shift ty a b shift) - (provide - (= result (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) - (switch (extract 15 8 shift) - ((ALUOp.Lsl) (bvshl (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsr) (bvlshr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Asr) (bvashr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))) - (bvadd a - (switch (extract 15 8 shift) - ((ALUOp.Lsl) (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsr) (bvlshr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Asr) (bvashr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))))) - -(decl add_shift (Type Reg Reg ShiftOpAndAmt) Reg) -(rule (add_shift ty x y z) (alu_rrr_shift (ALUOp.Add) ty x y z)) - -(spec (lshr_from_u64 ty a) - (provide (= result (concat (ALUOp.Lsr) (extract 7 0 a)))) - (require (= (extract 63 8 a) #b00000000000000000000000000000000000000000000000000000000))) -(decl pure lshr_from_u64 (Type Imm64) ShiftOpAndAmt) -(extern constructor lshr_from_u64 lshr_from_u64) - -;; BROKEN: using lshr_from_u64 instead of lshr_from_u64 -(rule 6 (lower (has_type (fits_in_64 ty) - (iadd (ishl x (iconst k)) y))) - (if-let amt (lshr_from_u64 ty k)) - (add_shift ty y x amt)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/imul/broken_imul.isle b/cranelift/isle/veri/veri_engine/examples/broken/imul/broken_imul.isle deleted file mode 100644 index afb301f8ef9c..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/imul/broken_imul.isle +++ /dev/null @@ -1,59 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum - ;; An ALU operation with two register sources and a register destination. - (AluRRR - (alu_op ALUOp) - (size OperandSize) -))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) -)) - -(type ALUOp3 - (enum - ;; Multiply-add - (MAdd) -)) - -(type OperandSize extern - (enum Size32 - Size64)) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -(decl alu_rrrr (ALUOp3 Type Reg Reg Reg) Reg) -(extern constructor alu_rrrr alu_rrrr) - -(spec (zero_reg) - (provide (= (zero_ext 64 #x0000000000000000) result))) -(decl zero_reg () Reg) -(extern constructor zero_reg zero_reg) - -;; Helper for calculating the `OperandSize` corresponding to a type -(decl operand_size (Type) OperandSize) -(rule (operand_size (fits_in_32 _ty)) (OperandSize.Size32)) -(rule (operand_size (fits_in_64 _ty)) (OperandSize.Size64)) - -;; Helpers for generating `madd` instructions. -(spec (madd ty a b c) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b)))) - (bvadd c (bvmul a b)))))) -(decl madd (Type Reg Reg Reg) Reg) -(rule (madd ty x y z) (alu_rrrr (ALUOp3.MAdd) ty x y z)) - -;; `i64` and smaller. -(rule (lower (has_type (fits_in_64 ty) (imul x y))) - (madd ty y y (zero_reg))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_base_case.isle b/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_base_case.isle deleted file mode 100644 index 5637496d61cb..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_base_case.isle +++ /dev/null @@ -1,27 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -(type ALUOp - (enum - (Sub) -)) - -(spec (sub ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (extract 31 0 b))) - (bvsub a b))))) -(decl sub (Type Reg Reg) Reg) -(extern constructor sub sub) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -(rule (lower (has_type (fits_in_64 ty) (isub x y))) - (sub ty y x)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12.isle b/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12.isle deleted file mode 100644 index 06d2590c57d1..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12.isle +++ /dev/null @@ -1,44 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Sub) -)) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -;; Broken: no require -(spec (imm12_from_value arg) - (provide - (= result (conv_to (widthof result) (zero_ext 64 arg))) - (= arg (conv_to (widthof arg) (zero_ext 64 result))))) -(decl imm12_from_value (Imm12) Value) -(extern extractor imm12_from_value imm12_from_value) - -(rule 0 (lower (has_type (fits_in_64 ty) (isub x (imm12_from_value y)))) - (sub_imm ty x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12neg.isle b/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12neg.isle deleted file mode 100644 index b9bbc1fa8b67..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12neg.isle +++ /dev/null @@ -1,50 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Add) -)) - -;; BROKEN: * instead of - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (add_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvmul (extract 31 0 a) (zero_ext 32 b))) - (bvmul a (zero_ext 64 b))))) - (require (or (<= (bv2int b) 4094) - (and (<= (bv2int b) 16773119) - (= (extract 2 0 b) #b000))))) -(decl add_imm (Type Reg Imm12) Reg) -(rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_negated_value arg) - (provide (or (bvule (bvneg (zero_ext 64 result)) #x0000000000000fff) - (and (bvule (bvneg (zero_ext 64 result)) #x0000000000fff000) - (= (extract 2 0 (bvneg (zero_ext 64 result))) #b000))) - (= result (conv_to (widthof result) (bvneg (zero_ext 64 arg)))))) -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) -(decl imm12_from_negated_value (Imm12) Value) -(extern extractor imm12_from_negated_value imm12_from_negated_value) - -(rule 2 (lower (has_type (fits_in_64 ty) (isub x (imm12_from_negated_value y)))) - (add_imm ty x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12neg_not_distinct.isle b/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12neg_not_distinct.isle deleted file mode 100644 index 762604f6443d..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_imm12neg_not_distinct.isle +++ /dev/null @@ -1,49 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Add) -)) - -(spec (add_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) (zero_ext 32 b))) - (bvadd a (zero_ext 64 b))))) - (require (or (<= (bv2int b) 4094) - (and (<= (bv2int b) 16773119) - (= (extract 2 0 b) #b000))))) -(decl add_imm (Type Reg Imm12) Reg) -(rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_negated_value arg) - (provide (or (bvult (bvneg (zero_ext 64 result)) #x0000000000000fff) - (and (bvult (bvneg (zero_ext 64 result)) #x0000000000fff000) - (= (extract 2 0 (bvneg (zero_ext 64 result))) #b000))) - (= result (conv_to (widthof result) (bvneg (zero_ext 64 arg)))))) -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) -(decl imm12_from_negated_value (Imm12) Value) -(extern extractor imm12_from_negated_value imm12_from_negated_value) - -;; BROKEN: for ty < 64, this only matches on zero -(rule 2 (lower (has_type (fits_in_64 ty) (isub x (imm12_from_negated_value y)))) - (add_imm ty x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_shift.isle b/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_shift.isle deleted file mode 100644 index 6b1143419325..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/isub/broken_shift.isle +++ /dev/null @@ -1,84 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum - (AluRRRShift - (shiftop ShiftOpAndAmt)) -)) - -;; ASSUMING 64 BIT MODE!!! -;; annotations will interpret this as an 10 bit field -;; the two msb encode the type of shift as follows: -;; 00: lsl -;; 01: lsr -;; 10: asr -;; 11: invalid -;; the rest will encode a 8-bit shift amount -(type ShiftOpAndAmt (primitive ShiftOpAndAmt)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -(type ALUOp (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndNot) - (Eor) - (EorNot) - (SubS) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl))) - - -(decl alu_rrr_shift (ALUOp Type Reg Reg ShiftOpAndAmt) Reg) -(extern constructor alu_rrr_shift alu_rrr_shift) - -;; BROKEN: swapped shl, shr -(spec (sub_shift ty a b shift) - (provide - (= result (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (switch (extract 15 8 shift) - ((ALUOp.Lsr) (bvshl (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvlshr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvashr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))) - (bvsub a (switch (extract 15 8 shift) - ((ALUOp.Lsr) (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvlshr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))) - ((ALUOp.Lsl) (bvashr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))))) - -(decl sub_shift (Type Reg Reg ShiftOpAndAmt) Reg) -(rule (sub_shift ty x y z) (alu_rrr_shift (ALUOp.Sub) ty x y z)) - -(spec (lshl_from_imm64 ty a) - (provide (= result (concat #x0e (extract 7 0 a)))) - (require (= (extract 63 8 a) #b00000000000000000000000000000000000000000000000000000000))) -(decl pure lshl_from_imm64 (Type Imm64) ShiftOpAndAmt) -(extern constructor lshl_from_imm64 lshl_from_imm64) - -(rule -3 (lower (has_type (fits_in_64 ty) - (isub x (ishl y (iconst k))))) - (if-let amt (lshl_from_imm64 ty k)) - (sub_shift ty x y amt)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/sdiv/broken_sdiv.isle b/cranelift/isle/veri/veri_engine/examples/broken/sdiv/broken_sdiv.isle deleted file mode 100644 index a43cdd8b78e9..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/sdiv/broken_sdiv.isle +++ /dev/null @@ -1,55 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst (enum)) - -(type ALUOp - (enum - (SDiv) -)) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -;; BROKEN: zero-extends instead of sign-extends -;; Place a `Value` into a register, sign extending it to 64-bits -(spec (put_in_reg_sext64 x) (provide (= (zero_ext 64 x) result))) -(decl put_in_reg_sext64 (Value) Reg) -(extern constructor put_in_reg_sext64 put_in_reg_sext64) - -;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero. - (spec (put_nonzero_in_reg_sext64 x) - (provide (= (sign_ext 64 x) result)) - (require (not (= #x0000000000000000 result)))) -(decl put_nonzero_in_reg_sext64 (Value) Reg) -(extern constructor put_nonzero_in_reg_sext64 put_nonzero_in_reg_sext64) - -(spec (a64_sdiv ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsdiv (extract 31 0 a) (extract 31 0 b))) - (bvsdiv a b))))) -(decl a64_sdiv (Type Reg Reg) Reg) -(rule (a64_sdiv ty x y) (alu_rrr (ALUOp.SDiv) ty x y)) - -;; Check for signed overflow. The only case is min_value / -1. -;; The following checks must be done in 32-bit or 64-bit, depending -;; on the input type. -(spec (trap_if_div_overflow ty x y) - (provide (= x result) - (if (= ty 32) - (not (and (= #x00000000 (extract 31 0 y)) - (= #x80000000 (extract 31 0 y)))) - (not (and (= #x0000000000000000 y) - (= #x8000000000000000 y)))))) -(decl trap_if_div_overflow (Type Reg Reg) Reg) -(extern constructor trap_if_div_overflow trap_if_div_overflow) - -(rule (lower (has_type (fits_in_64 ty) (sdiv x y))) - (let ((x64 Reg (put_in_reg_sext64 x)) - (y64 Reg (put_nonzero_in_reg_sext64 y)) - (valid_x64 Reg (trap_if_div_overflow ty x64 y64)) - (result Reg (a64_sdiv $I64 valid_x64 y64))) - result)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/sdiv/broken_sdiv_safe_const.isle b/cranelift/isle/veri/veri_engine/examples/broken/sdiv/broken_sdiv_safe_const.isle deleted file mode 100644 index bb9d27089e48..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/sdiv/broken_sdiv_safe_const.isle +++ /dev/null @@ -1,76 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst (enum)) - -(type ALUOp - (enum - (UDiv) -)) - -;; Model ImmExtend as an Int, where -;; Sign == 1 and Zero == 0 -(type ImmExtend - (enum - (Zero) - (Sign) -)) - -(model ImmExtend - (enum - (Sign #b0) - (Zero #b1))) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -(spec (a64_udiv ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvudiv (extract 31 0 a) (extract 31 0 b))) - (bvudiv a b))))) -(decl a64_udiv (Type Reg Reg) Reg) -(rule (a64_udiv ty x y) (alu_rrr (ALUOp.UDiv) ty x y)) - -(spec (imm ty ext x) - (provide - (= result - (switch ty - (8 (if (= ext #b1) (zero_ext 64 (extract 7 0 x)) (sign_ext 64 (extract 7 0 x)))) - (16 (if (= ext #b1) (zero_ext 64 (extract 15 0 x)) (sign_ext 64 (extract 15 0 x)))) - (32 (if (= ext #b1) (zero_ext 64 (extract 32 0 x)) (sign_ext 64 (extract 32 0 x)))) - (64 x)))) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) -(decl imm (Type ImmExtend u64) Reg) -(extern constructor imm imm) - -;; Place a `Value` into a register, sign extending it to 64-bits -(spec (put_in_reg_sext64 x) (provide (= (sign_ext 64 x) result))) -(decl put_in_reg_sext64 (Value) Reg) -(extern constructor put_in_reg_sext64 put_in_reg_sext64) - -;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero. -;; Broken: missing require - (spec (put_nonzero_in_reg_sext64 x) - (provide (= (sign_ext 64 x) result)) - ;; (require (not (= #x0000000000000000 result))) - ) -(decl put_nonzero_in_reg_sext64 (Value) Reg) -(extern constructor put_nonzero_in_reg_sext64 put_nonzero_in_reg_sext64) - -;; Helper for extracting an immediate that's not 0 and not -1 from an imm64. -;; (spec (safe_divisor_from_imm64 x) -;; (provide (= (sign_ext 64 x) result)) -;; (require (not (= #x0000000000000000 result)) -;; (not (= #x1111111111111111 result)))) -;; (decl safe_divisor_from_imm64 (u64) Imm64) -;; (extern extractor safe_divisor_from_imm64 safe_divisor_from_imm64) - -;; Special case for `sdiv` where no checks are needed due to division by a -;; constant meaning the checks are always passed. -;; BROKEN: uses udiv instead of sdiv -(rule sdiv_safe_divisor 1 (lower (has_type (fits_in_64 ty) (sdiv x (iconst imm)))) - (if-let y (safe_divisor_from_imm64 ty imm)) - (a64_udiv $I64 (put_in_reg_sext64 x) (imm ty (ImmExtend.Sign) y))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_do_shift_32.isle b/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_do_shift_32.isle deleted file mode 100644 index bec3e94147fa..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_do_shift_32.isle +++ /dev/null @@ -1,111 +0,0 @@ -;; Instruction formats. -(type MInst - (enum -)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndS) - (AndNot) - ;; XOR (AArch64 calls this "EOR") - (Eor) - ;; XNOR (AArch64 calls this "EOR-NOT") - (EorNot) - ;; Add, setting flags - (AddS) - ;; Sub setting flags - (SubS) - ;; Signed multiplyhigh-word result - (SMulH) - ;; Unsigned multiplyhigh-word result - (UMulH) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl) - ;; Add with carry - (Adc) - ;; Add with carrysettings flags - (AdcS) - ;; Subtract with carry - (Sbc) - ;; Subtract with carrysettings flags - (SbcS) -)) - -;; BROKEN: no restriction on op in annotation - (spec (do_shift op t a b) - (provide - (= result - (switch op - ((ALUOp.Lsr) (conv_to 64 - (bvlshr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Asr) (conv_to 64 - (bvashr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Lsl) (conv_to 64 - (bvshl (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b))))))))) - (require - (= t (widthof b)) - (or (= t 8) (= t 16) (= t 32) (= t 64)))) -(instantiate do_shift - ((args (bv 8) Int (bv 64) (bv 8)) (ret (bv 64)) (canon (bv 8))) - ((args (bv 8) Int (bv 64) (bv 16)) (ret (bv 64)) (canon (bv 16))) - ((args (bv 8) Int (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 8) Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) -(decl do_shift (ALUOp Type Reg Value) Reg) - -(spec (alu_rrr op t a b) - (provide - (= result (switch op - ((ALUOp.Lsr) - (if (<= t 32) - (conv_to 64 (bvlshr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b)))) - (bvlshr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b)))) - ((ALUOp.Asr) - (if (<= t 32) - (conv_to 64 (bvashr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b)))) - (bvashr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b)))) - ((ALUOp.Lsl) - (if (<= t 32) - (conv_to 64 (bvshl (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b)))) - (bvshl a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b))))))) - (require - (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl))) - (or (= t 8) (= t 16) (= t 32) (= t 64)))) -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -(rule (do_shift op $I32 x y) (alu_rrr op $I32 x (value_regs_get y 0))) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_ishl_to_do_shift_64.isle b/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_ishl_to_do_shift_64.isle deleted file mode 100644 index 96d02c2c8d61..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_ishl_to_do_shift_64.isle +++ /dev/null @@ -1,110 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndS) - (AndNot) - ;; XOR (AArch64 calls this "EOR") - (Eor) - ;; XNOR (AArch64 calls this "EOR-NOT") - (EorNot) - ;; Add, setting flags - (AddS) - ;; Sub setting flags - (SubS) - ;; Signed multiplyhigh-word result - (SMulH) - ;; Unsigned multiplyhigh-word result - (UMulH) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl) - ;; Add with carry - (Adc) - ;; Add with carrysettings flags - (AdcS) - ;; Subtract with carry - (Sbc) - ;; Subtract with carrysettings flags - (SbcS) -)) - - (spec (do_shift op t a b) - (provide - (= result - (switch op - ((ALUOp.Lsr) (conv_to 64 - (bvlshr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Asr) (conv_to 64 - (bvashr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Lsl) (conv_to 64 - (bvshl (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b))))))))) - (require - (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl))) - (= t (widthof b)) - (or (= t 8) (= t 16) (= t 32) (= t 64)) - (switch op - ((ALUOp.Lsr) (switch t - (8 (= (extract 31 0 a) (zero_ext 32 (extract 7 0 a)))) - (16 (= (extract 31 0 a) (zero_ext 32 (extract 15 0 a)))) - (32 true) - (64 true))) - ((ALUOp.Asr) (switch t - (8 (= (extract 31 0 a) (sign_ext 32 (extract 7 0 a)))) - (16 (= (extract 31 0 a) (sign_ext 32 (extract 15 0 a)))) - (32 true) - (64 true))) - ((ALUOp.Lsl) true)))) -(instantiate do_shift - ((args (bv 8) Int (bv 64) (bv 8)) (ret (bv 64)) (canon (bv 8))) - ((args (bv 8) Int (bv 64) (bv 16)) (ret (bv 64)) (canon (bv 16))) - ((args (bv 8) Int (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 8) Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) -(decl do_shift (ALUOp Type Reg Value) Reg) -(extern constructor do_shift do_shift) - -;; BROKEN: Asr instead of Lsl -;; Shift for i64. -(rule (lower (has_type $I64 (ishl x y))) - (do_shift (ALUOp.Asr) $I64 x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_sshr_to_do_shift_fits_in_32.isle b/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_sshr_to_do_shift_fits_in_32.isle deleted file mode 100644 index 29d11e2db026..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_sshr_to_do_shift_fits_in_32.isle +++ /dev/null @@ -1,108 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndS) - (AndNot) - ;; XOR (AArch64 calls this "EOR") - (Eor) - ;; XNOR (AArch64 calls this "EOR-NOT") - (EorNot) - ;; Add, setting flags - (AddS) - ;; Sub setting flags - (SubS) - ;; Signed multiplyhigh-word result - (SMulH) - ;; Unsigned multiplyhigh-word result - (UMulH) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl) - ;; Add with carry - (Adc) - ;; Add with carrysettings flags - (AdcS) - ;; Subtract with carry - (Sbc) - ;; Subtract with carrysettings flags - (SbcS) -)) - -;; BROKEN: missing extension part of the spec - (spec (do_shift op t a b) - (provide - (= result - (switch op - ((ALUOp.Lsr) (conv_to 64 - (bvlshr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Asr) (conv_to 64 - (bvashr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Lsl) (conv_to 64 - (bvshl (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b))))))))) - (require - (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl))) - (= t (widthof b)) - (or (= t 8) (= t 16) (= t 32) (= t 64)))) -(instantiate do_shift - ((args (bv 8) Int (bv 64) (bv 8)) (ret (bv 64)) (canon (bv 8))) - ((args (bv 8) Int (bv 64) (bv 16)) (ret (bv 64)) (canon (bv 16))) - ((args (bv 8) Int (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 8) Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) -(decl do_shift (ALUOp Type Reg Value) Reg) -(extern constructor do_shift do_shift) - -(spec (put_in_reg_sext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (sign_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_sext32 (Value) Reg) -(extern constructor put_in_reg_sext32 put_in_reg_sext32) - -;; BROKEN: Wrong opcode -;; Shift for i64. -(rule -2 (lower (has_type (fits_in_32 ty) (sshr x y))) - (do_shift (ALUOp.Lsr) ty (put_in_reg_sext32 x) y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_ushr_to_do_shift_fits_in_32.isle b/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_ushr_to_do_shift_fits_in_32.isle deleted file mode 100644 index 377260a6799f..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/shifts/broken_ushr_to_do_shift_fits_in_32.isle +++ /dev/null @@ -1,120 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst - (enum -)) - -(model ALUOp (enum - (Add #x00) ;; 0 - (Sub #x01) - (Orr #x02) - (OrrNot #x03) - (And #x04) - (AndNot #x05) - (Eor #x06) - (EorNot #x07) - (SubS #x08) - (SDiv #x09) - (UDiv #x0a) - (RotR #x0b) - (Lsr #x0c) - (Asr #x0d) - (Lsl #x0e))) - -;; An ALU operation. This can be paired with several instruction formats -;; below (see `Inst`) in any combination. -(type ALUOp - (enum - (Add) - (Sub) - (Orr) - (OrrNot) - (And) - (AndS) - (AndNot) - ;; XOR (AArch64 calls this "EOR") - (Eor) - ;; XNOR (AArch64 calls this "EOR-NOT") - (EorNot) - ;; Add, setting flags - (AddS) - ;; Sub setting flags - (SubS) - ;; Signed multiplyhigh-word result - (SMulH) - ;; Unsigned multiplyhigh-word result - (UMulH) - (SDiv) - (UDiv) - (RotR) - (Lsr) - (Asr) - (Lsl) - ;; Add with carry - (Adc) - ;; Add with carrysettings flags - (AdcS) - ;; Subtract with carry - (Sbc) - ;; Subtract with carrysettings flags - (SbcS) -)) - - (spec (do_shift op t a b) - (provide - (= result - (switch op - ((ALUOp.Lsr) (conv_to 64 - (bvlshr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Asr) (conv_to 64 - (bvashr (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b)))))) - ((ALUOp.Lsl) (conv_to 64 - (bvshl (conv_to t a) - (conv_to t (zero_ext 64 - (bvand (conv_to (widthof b) (bvsub (int2bv 64 (widthof b)) #x0000000000000001)) b))))))))) - (require - (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl))) - (= t (widthof b)) - (or (= t 8) (= t 16) (= t 32) (= t 64)) - (switch op - ((ALUOp.Lsr) (switch t - (8 (= (extract 31 0 a) (zero_ext 32 (extract 7 0 a)))) - (16 (= (extract 31 0 a) (zero_ext 32 (extract 15 0 a)))) - (32 true) - (64 true))) - ((ALUOp.Asr) (switch t - (8 (= (extract 31 0 a) (sign_ext 32 (extract 7 0 a)))) - (16 (= (extract 31 0 a) (sign_ext 32 (extract 15 0 a)))) - (32 true) - (64 true))) - ((ALUOp.Lsl) true)))) -(instantiate do_shift - ((args (bv 8) Int (bv 64) (bv 8)) (ret (bv 64)) (canon (bv 8))) - ((args (bv 8) Int (bv 64) (bv 16)) (ret (bv 64)) (canon (bv 16))) - ((args (bv 8) Int (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 8) Int (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) -(decl do_shift (ALUOp Type Reg Value) Reg) -(extern constructor do_shift do_shift) - -;; Place a `Value` into a register, zero extending it to 32-bits -(spec (put_in_reg_zext32 arg) - (provide - (= result - (if (<= (widthof arg) 32) - (conv_to 64 (zero_ext 32 arg)) - (conv_to 64 arg))))) -(decl put_in_reg_zext32 (Value) Reg) -(extern constructor put_in_reg_zext32 put_in_reg_zext32) - -;; BROKEN: wrong op -;; Shift for i8/i16/i32. -(rule -1 (lower (has_type (fits_in_32 ty) (ushr x y))) - (do_shift (ALUOp.Lsl) ty (put_in_reg_zext32 x) y)) diff --git a/cranelift/isle/veri/veri_engine/examples/broken/udiv/broken_udiv.isle b/cranelift/isle/veri/veri_engine/examples/broken/udiv/broken_udiv.isle deleted file mode 100644 index 02f7d59dd1c2..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/udiv/broken_udiv.isle +++ /dev/null @@ -1,37 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst (enum)) - -(type ALUOp - (enum - (UDiv) -)) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -(spec (a64_udiv ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvudiv (extract 31 0 a) (extract 31 0 b))) - (bvudiv a b))))) -(decl a64_udiv (Type Reg Reg) Reg) -(rule (a64_udiv ty x y) (alu_rrr (ALUOp.UDiv) ty x y)) - -(spec (put_nonzero_in_reg_zext64 x) - (provide (= result (zero_ext 64 x))) - (require (not (= result #x0000000000000000)))) -(decl put_nonzero_in_reg_zext64 (Value) Reg) -(extern constructor put_nonzero_in_reg_zext64 put_nonzero_in_reg_zext64) - -(spec (put_in_reg_sext64 x) (provide (= (sign_ext 64 x) result))) -(decl put_in_reg_sext64 (Value) Reg) -(extern constructor put_in_reg_sext64 put_in_reg_sext64) - -;; Note that aarch64's `udiv` doesn't trap so to respect the semantics of -;; CLIF's `udiv` the check for zero needs to be manually performed. -(rule (lower (has_type (fits_in_64 ty) (udiv x y))) - (a64_udiv $I64 (put_in_reg_sext64 x) (put_nonzero_in_reg_zext64 y))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/udiv/udiv_cve.isle b/cranelift/isle/veri/veri_engine/examples/broken/udiv/udiv_cve.isle deleted file mode 100644 index bdc0504bef12..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/udiv/udiv_cve.isle +++ /dev/null @@ -1,41 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst (enum)) - -(type ALUOp - (enum - (SDiv) -)) - -(decl alu_rrr (ALUOp Type Reg Reg) Reg) -(extern constructor alu_rrr alu_rrr) - -;; Helper for generating `udiv` instructions. -(spec (a64_udiv ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvudiv (extract 31 0 a) (extract 31 0 b))) - (bvudiv a b))))) -(decl a64_udiv (Type Reg Reg) Reg) -(extern constructor a64_udiv a64_udiv) - -(spec (imm ty x) (provide (= result (sign_ext 64 (conv_to ty x))))) -(decl imm (Type u64) Reg) -(extern constructor imm imm) - -(spec (put_in_reg_zext64 x) (provide (= result (zero_ext 64 x)))) -(decl put_in_reg_zext64 (Value) Reg) -(extern constructor put_in_reg_zext64 put_in_reg_zext64) - -;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero. -(spec (put_nonzero_in_reg_zext64 x) - (provide (= result (zero_ext 64 x))) - (require (not (= result #x0000000000000000)))) -(decl put_nonzero_in_reg_zext64 (Value) Reg) -(extern constructor put_nonzero_in_reg_zext64 put_nonzero_in_reg_zext64) - -(rule udiv (lower (has_type (fits_in_64 ty) (udiv x y))) - (a64_udiv $I64 (put_in_reg_zext64 x) (put_nonzero_in_reg_zext64 y))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/broken/udiv/udiv_cve_underlying.isle b/cranelift/isle/veri/veri_engine/examples/broken/udiv/udiv_cve_underlying.isle deleted file mode 100644 index 1c913193c416..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/broken/udiv/udiv_cve_underlying.isle +++ /dev/null @@ -1,26 +0,0 @@ - -(type MInst (enum)) - -(spec (integral_ty ty) - (provide (= result ty)) - (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64)))) -(decl integral_ty (Type) Type) -(extern extractor integral_ty integral_ty) - -;; Try changing this "sign_ext" to "zero_ext": the test fails either way -;; (spec (imm ty x) (provide (= result (zero_ext 64 (conv_to ty x))))) -(spec (imm ty x) (provide (= result (sign_ext 64 (conv_to ty x))))) -(instantiate imm - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 8))) - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 16))) - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 32))) - ((args Int (bv 64)) (ret (bv 64)) (canon (bv 64))) -) -(decl imm (Type u64) Reg) - -(spec (load_constant64_full x) (provide (= result x))) -(decl load_constant64_full (u64) Reg) -(extern constructor load_constant64_full load_constant64_full) - -(rule (imm (integral_ty _ty) n) - (load_constant64_full n)) diff --git a/cranelift/isle/veri/veri_engine/examples/constructs/if-let.isle b/cranelift/isle/veri/veri_engine/examples/constructs/if-let.isle deleted file mode 100644 index 658116cd179b..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/constructs/if-let.isle +++ /dev/null @@ -1,23 +0,0 @@ -(spec (lower arg) (provide (= result arg))) -(decl lower (Inst) InstOutput) - -;; Instruction formats. -(type MInst (enum)) - -;; Constructor to test whether two values are same. -(spec (same_value x y) (provide (= result x ) (= x y))) -(decl pure same_value (Value Value) Value) -(extern constructor same_value same_value) - -(rule (lower (has_type (fits_in_64 ty) (iadd x y))) - (if-let z (same_value x y)) - (add ty z z)) - -(spec (add ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) (extract 31 0 b))) - (bvadd a b))))) -(decl add (Type Reg Reg) Reg) -(extern constructor add add) diff --git a/cranelift/isle/veri/veri_engine/examples/iadd/updated_iadd_imm12neg_left.isle b/cranelift/isle/veri/veri_engine/examples/iadd/updated_iadd_imm12neg_left.isle deleted file mode 100644 index 53f33cb77e37..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/iadd/updated_iadd_imm12neg_left.isle +++ /dev/null @@ -1,49 +0,0 @@ -(spec (lower arg) (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Sub) -)) - -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_negated_value arg) - (provide (or (bvult (bvneg (sign_ext 64 arg)) #x0000000000000fff) - (and (bvult (bvneg (sign_ext 64 arg)) #x0000000000fff000) - (= (extract 2 0 (bvneg (sign_ext 64 arg))) #b000))) - (= result (extract 23 0 (bvneg (sign_ext 64 arg)))))) -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) -(decl pure partial imm12_from_negated_value (Value) Imm12) -(extern extractor imm12_from_negated_value imm12_from_negated_value) - -(rule 3 (lower (has_type (fits_in_64 ty) (iadd x y))) - (if-let imm12_neg (imm12_from_negated_value x)) - (sub_imm ty y imm12_neg)) diff --git a/cranelift/isle/veri/veri_engine/examples/iadd/updated_iadd_imm12neg_right.isle b/cranelift/isle/veri/veri_engine/examples/iadd/updated_iadd_imm12neg_right.isle deleted file mode 100644 index 79660ff3d235..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/iadd/updated_iadd_imm12neg_right.isle +++ /dev/null @@ -1,49 +0,0 @@ -(spec (lower arg) (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Sub) -)) - -(spec (sub_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b))) - (bvsub a (zero_ext 64 b))))) - (require (or (bvult (zero_ext 64 b) #x0000000000000fff) - (and (bvult (zero_ext 64 b) #x0000000000fff000) - (= (extract 2 0 (zero_ext 64 b)) #b000))) - (= (widthof b) 24))) -(decl sub_imm (Type Reg Imm12) Reg) -(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_negated_value arg) - (provide (or (bvult (bvneg (sign_ext 64 arg)) #x0000000000000fff) - (and (bvult (bvneg (sign_ext 64 arg)) #x0000000000fff000) - (= (extract 2 0 (bvneg (sign_ext 64 arg))) #b000))) - (= result (extract 23 0 (bvneg (sign_ext 64 arg)))))) -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) -(decl pure partial imm12_from_negated_value (Value) Imm12) -(extern extractor imm12_from_negated_value imm12_from_negated_value) - -(rule 2 (lower (has_type (fits_in_64 ty) (iadd x y))) - (if-let imm12_neg (imm12_from_negated_value y)) - (sub_imm ty x imm12_neg)) diff --git a/cranelift/isle/veri/veri_engine/examples/isub/imm12neg_new.isle b/cranelift/isle/veri/veri_engine/examples/isub/imm12neg_new.isle deleted file mode 100644 index 64e8436a25fa..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/isub/imm12neg_new.isle +++ /dev/null @@ -1,50 +0,0 @@ -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(type MInst - (enum -)) - -;; Imm12 modeled as the range of intermediates it can represent. -(model Imm12 (type (bv 24))) -(type Imm12 (primitive Imm12)) - -(type ALUOp - (enum - (Add) -)) - -;; Note that 4094 = 0xffe and 16773119 = 0xffefff -(spec (add_imm ty a b) - (provide - (= result - (if (<= ty 32) - (conv_to 64 (bvadd (extract 31 0 a) (zero_ext 32 b))) - (bvadd a (zero_ext 64 b))))) - (require (or (<= (bv2int b) 4094) - (and (<= (bv2int b) 16773119) - (= (extract 2 0 b) #b000))))) -(decl add_imm (Type Reg Imm12) Reg) -(rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) - -(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(extern constructor alu_rr_imm12 alu_rr_imm12) - -(spec (imm12_from_negated_value arg) - (provide (or (bvult (bvneg (sign_ext 64 arg)) #x0000000000000fff) - (and (bvult (bvneg (sign_ext 64 arg)) #x0000000000fff000) - (= (extract 2 0 (bvneg (sign_ext 64 arg))) #b000))) - (= result (extract 23 0 (bvneg (sign_ext 64 arg)))))) -(instantiate imm12_from_negated_value - ((args (bv 8)) (ret (bv 24)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 24)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 24)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 24)) (canon (bv 64))) -) -(decl pure partial imm12_from_negated_value (Value) Imm12) -(extern extractor imm12_from_negated_value imm12_from_negated_value) - -(rule isub_imm12_neg 2 (lower (has_type (fits_in_64 ty) (isub x y))) - (if-let imm12_neg (imm12_from_negated_value y)) - (add_imm ty x imm12_neg)) diff --git a/cranelift/isle/veri/veri_engine/examples/load/load_add_panic.isle b/cranelift/isle/veri/veri_engine/examples/load/load_add_panic.isle deleted file mode 100644 index 4d5bbea707e4..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/load/load_add_panic.isle +++ /dev/null @@ -1,24 +0,0 @@ -;; Instruction formats -(type MInst (enum)) - -;; Add with 2 loads spec -(form - lhs_form - ((args (bv 8) (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16) (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 32) (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) -(spec (lhs x y) - (provide (= result (bvadd (load_effect #x0000 (widthof x) x) (load #x0000 (widthof y) y))))) -(decl lhs (Value Value) Inst) -(extern extractor lhs lhs) -(instantiate lhs lhs_form) - -(spec (rhs x y) - (provide (= result (bvadd (load_effect #x0000 (widthof x) x) (load #x0000 (widthof y) y))))) -(decl rhs (Value Value) Inst) -(extern constructor rhs rhs) - -(rule (lhs x y) - (rhs x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/load/load_conditional.isle b/cranelift/isle/veri/veri_engine/examples/load/load_conditional.isle deleted file mode 100644 index db2fa6867ef9..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/load/load_conditional.isle +++ /dev/null @@ -1,24 +0,0 @@ -;; Instruction formats. -(type MInst (enum)) - -(form - lhs_form - ((args Bool (bv 8) (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args Bool (bv 16) (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args Bool (bv 32) (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args Bool (bv 64) (bv 64)) (ret (bv 64)) (canon (bv 64))) -) - -(spec (lhs cond x y) - (provide (= result (load_effect #x0000 (widthof (if cond x y)) (if cond x y))))) -(decl lhs (bool Value Value) Inst) -(extern extractor lhs lhs) -(instantiate lhs lhs_form) - -(spec (rhs x y) - (provide (= result (load_effect #x0000 (widthof x) x)))) -(decl rhs (Value Value) Inst) -(extern constructor rhs rhs) - -(rule (lhs true x y) - (rhs x y)) diff --git a/cranelift/isle/veri/veri_engine/examples/mid-end/broken_bor_band_consts.isle b/cranelift/isle/veri/veri_engine/examples/mid-end/broken_bor_band_consts.isle deleted file mode 100644 index c0363cf548c9..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/mid-end/broken_bor_band_consts.isle +++ /dev/null @@ -1,72 +0,0 @@ -(type Type (primitive Type)) -(type Value (primitive Value)) -(type Imm64 (primitive Imm64)) -(extern const true bool) - -(spec (simplify x) (provide (= x result))) -(instantiate simplify - ((args (bv 8)) (ret (bv 8)) (canon (bv 8))) - ((args (bv 16)) (ret (bv 16)) (canon (bv 16))) - ((args (bv 32)) (ret (bv 32)) (canon (bv 32))) - ((args (bv 64)) (ret (bv 64)) (canon (bv 64))) -) -(decl simplify (Value) Value) - -(spec (bor ty x y) - (provide (= (bvor x y) result)) - (require - (= ty (widthof x)) - (= ty (widthof y)))) -(decl bor (Type Value Value) Value) -(extern extractor bor bor) -(extern constructor bor bor) - -(spec (bnot ty x) - (provide (= (bvnot x) result)) - (require (= ty (widthof x)))) -(decl bnot (Type Value) Value) -(extern extractor bnot bnot) -(extern constructor bnot bnot) - -(spec (band ty x y) - (provide (= (bvand x y) result)) - (require - (= ty (widthof x)) - (= ty (widthof y)))) -(decl band (Type Value Value) Value) -(extern extractor band band) -(extern constructor band band) - -;; Specify to this rule with constants -(spec (iconst ty arg) - (provide (= arg (zero_ext 64 result))) - (require (= ty (widthof arg)))) -(decl iconst (Type Imm64) Value) -(extern constructor iconst iconst) -(extern extractor iconst iconst) - -;; Extract a `u64` from an `Imm64`. -(spec (u64_from_imm64 arg) (provide (= arg result))) -(decl u64_from_imm64 (u64) Imm64) -(extern extractor u64_from_imm64 u64_from_imm64) - -(spec (u64_eq x y) - (provide (= result (if (= x y) #x0000000000000000 #x0000000000000001)))) -(decl pure u64_eq (u64 u64) u64) -(extern constructor u64_eq u64_eq) - -(spec (u64_not arg) (provide (= (bvnot arg) result))) -(decl pure u64_not (u64) u64) -(extern constructor u64_not u64_not) - -;; `or(and(x, y), noty) == or(x, not(y))` specialized for constants, since -;; otherwise we may not know that `z == not(y)` since we don't generally expand -;; constants in the e-graph. -;; -;; (No need to duplicate for commutative `bor` for this constant version because -;; we move constants to the right.) -(rule (simplify (bor ty - (band ty x (iconst ty (u64_from_imm64 y))) - z @ (iconst ty (u64_from_imm64 zk)))) - (if (u64_eq zk (u64_not y))) - (bor ty x z)) diff --git a/cranelift/isle/veri/veri_engine/examples/store/broken_bvsub_store_with_load.isle b/cranelift/isle/veri/veri_engine/examples/store/broken_bvsub_store_with_load.isle deleted file mode 100644 index a16efcf048f7..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/store/broken_bvsub_store_with_load.isle +++ /dev/null @@ -1,104 +0,0 @@ -(type MInst (enum)) - -(type SinkableLoad extern (enum)) - -(type OperandSize extern - (enum Size8 - Size16 - Size32 - Size64)) - -(type Amode (enum - ;; Immediate sign-extended and a register - (ImmReg (simm32 i32) - (base Reg) - (flags MemFlagsData)) - - ;; Sign-extend-32-to-64(simm32) + base + (index << shift) - (ImmRegRegShift (simm32 i32) - (base Gpr) - (index Gpr) - (shift u8) - (flags MemFlagsData)) - - ;; Sign-extend-32-to-64(immediate) + RIP (instruction - ;; pointer). The appropriate relocation is emitted so - ;; that the resulting immediate makes this Amode refer to - ;; the given MachLabel. - (RipRelative (target MachLabel)))) - -(type Gpr (primitive Gpr)) - -(type RegMemImm extern - (enum - (Reg (reg Reg)) - (Mem (addr SyntheticAmode)) - (Imm (simm32 u32)))) - -(type SyntheticAmode extern (enum)) - -(convert SinkableLoad RegMemImm sink_load_to_reg_mem_imm) - -(convert Value Gpr put_in_gpr) - -(decl x64_add_mem (Type Amode Gpr) SideEffectNoResult) -(spec (x64_add_mem ty addr val) - (provide (= result (store_effect - (extract 79 64 addr) - ty - (conv_to ty (bvsub (load_effect (extract 79 64 addr) ty (extract 63 0 addr)) (conv_to ty val))) - (extract 63 0 addr)) - ) - ) - (require (or (= ty 32) (= ty 64))) -) -(extern constructor x64_add_mem x64_add_mem) - -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(decl sinkable_load (SinkableLoad) Value) -(spec (sinkable_load inst) - (provide (= result inst))) -(extern extractor sinkable_load sinkable_load) - -(decl sink_load_to_reg_mem_imm (SinkableLoad) RegMemImm) -(spec (sink_load_to_reg_mem_imm load) - (provide (= result load))) -(extern constructor sink_load_to_reg_mem_imm sink_load_to_reg_mem_imm) - -(spec (put_in_gpr arg) (provide (= result (conv_to 64 arg)))) -(decl put_in_gpr (Value) Gpr) -(extern constructor put_in_gpr put_in_gpr) - -(spec (to_amode flags val offset) - (provide (= result (concat flags (bvadd val (sign_ext 64 offset))))) - (require - (= (widthof val) 64))) -(decl to_amode (MemFlagsData Value Offset32) Amode) -(extern constructor to_amode to_amode) - -(decl operand_size_of_type_32_64 (Type) OperandSize) -(extern constructor operand_size_of_type_32_64 operand_size_of_type_32_64) - -(form store - ((args (bv 16) (bv 8) (bv 64) (bv 32)) (ret Unit) (canon (bv 8))) - ((args (bv 16) (bv 16) (bv 64) (bv 32)) (ret Unit) (canon (bv 16))) - ((args (bv 16) (bv 32) (bv 64) (bv 32)) (ret Unit) (canon (bv 32))) - ((args (bv 16) (bv 64) (bv 64) (bv 32)) (ret Unit) (canon (bv 64))) -) - - -(rule store_x64_add_mem 3 (lower - (store flags - (has_type (ty_32_or_64 ty) - (iadd (and - (sinkable_load sink) - (load flags addr offset)) - src2)) - addr - offset)) - (let ((_ RegMemImm sink)) - (side_effect - (x64_add_mem ty (to_amode flags addr offset) src2)))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/store/broken_isub_store_with_load.isle b/cranelift/isle/veri/veri_engine/examples/store/broken_isub_store_with_load.isle deleted file mode 100644 index 2dfed3114f2e..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/store/broken_isub_store_with_load.isle +++ /dev/null @@ -1,104 +0,0 @@ -(type MInst (enum)) - -(type SinkableLoad extern (enum)) - -(type OperandSize extern - (enum Size8 - Size16 - Size32 - Size64)) - -(type Amode (enum - ;; Immediate sign-extended and a register - (ImmReg (simm32 i32) - (base Reg) - (flags MemFlagsData)) - - ;; Sign-extend-32-to-64(simm32) + base + (index << shift) - (ImmRegRegShift (simm32 i32) - (base Gpr) - (index Gpr) - (shift u8) - (flags MemFlagsData)) - - ;; Sign-extend-32-to-64(immediate) + RIP (instruction - ;; pointer). The appropriate relocation is emitted so - ;; that the resulting immediate makes this Amode refer to - ;; the given MachLabel. - (RipRelative (target MachLabel)))) - -(type Gpr (primitive Gpr)) - -(type RegMemImm extern - (enum - (Reg (reg Reg)) - (Mem (addr SyntheticAmode)) - (Imm (simm32 u32)))) - -(type SyntheticAmode extern (enum)) - -(convert SinkableLoad RegMemImm sink_load_to_reg_mem_imm) - -(convert Value Gpr put_in_gpr) - -(decl x64_add_mem (Type Amode Gpr) SideEffectNoResult) -(spec (x64_add_mem ty addr val) - (provide (= result (store_effect - (extract 79 64 addr) - ty - (conv_to ty (bvadd (load_effect (extract 79 64 addr) ty (extract 63 0 addr)) (conv_to ty val))) - (extract 63 0 addr)) - ) - ) - (require (or (= ty 32) (= ty 64))) -) -(extern constructor x64_add_mem x64_add_mem) - -(spec (lower arg) - (provide (= result arg))) -(decl lower (Inst) InstOutput) - -(decl sinkable_load (SinkableLoad) Value) -(spec (sinkable_load inst) - (provide (= result inst))) -(extern extractor sinkable_load sinkable_load) - -(decl sink_load_to_reg_mem_imm (SinkableLoad) RegMemImm) -(spec (sink_load_to_reg_mem_imm load) - (provide (= result load))) -(extern constructor sink_load_to_reg_mem_imm sink_load_to_reg_mem_imm) - -(spec (put_in_gpr arg) (provide (= result (conv_to 64 arg)))) -(decl put_in_gpr (Value) Gpr) -(extern constructor put_in_gpr put_in_gpr) - -(spec (to_amode flags val offset) - (provide (= result (concat flags (bvadd val (sign_ext 64 offset))))) - (require - (= (widthof val) 64))) -(decl to_amode (MemFlagsData Value Offset32) Amode) -(extern constructor to_amode to_amode) - -(decl operand_size_of_type_32_64 (Type) OperandSize) -(extern constructor operand_size_of_type_32_64 operand_size_of_type_32_64) - -(form store - ((args (bv 16) (bv 8) (bv 64) (bv 32)) (ret Unit) (canon (bv 8))) - ((args (bv 16) (bv 16) (bv 64) (bv 32)) (ret Unit) (canon (bv 16))) - ((args (bv 16) (bv 32) (bv 64) (bv 32)) (ret Unit) (canon (bv 32))) - ((args (bv 16) (bv 64) (bv 64) (bv 32)) (ret Unit) (canon (bv 64))) -) - - -(rule store_x64_add_mem 3 (lower - (store flags - (has_type (ty_32_or_64 ty) - (isub (and - (sinkable_load sink) - (load flags addr offset)) - src2)) - addr - offset)) - (let ((_ RegMemImm sink)) - (side_effect - (x64_add_mem ty (to_amode flags addr offset) src2)))) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/store/store_switch.isle b/cranelift/isle/veri/veri_engine/examples/store/store_switch.isle deleted file mode 100644 index 7b0e9cf3c401..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/store/store_switch.isle +++ /dev/null @@ -1,25 +0,0 @@ -;; Instruction formats -(type MInst (enum)) - -(form - lhs_form - ((args Bool (bv 8) (bv 8)) (ret Unit) (canon (bv 8))) - ((args Bool (bv 16) (bv 16)) (ret Unit) (canon (bv 16))) - ((args Bool (bv 32) (bv 32)) (ret Unit) (canon (bv 32))) - ((args Bool (bv 64) (bv 64)) (ret Unit) (canon (bv 64))) -) - -(spec (lhs cond val2 val3) - (provide (= result (store_effect #x0000 (widthof (switch cond (true val2) (false val3))) (switch cond (true val2) (false val3)) #x0000000000000000))) - ) -(decl lhs (bool Value Value) Inst) -(extern extractor lhs lhs) -(instantiate lhs lhs_form) - -(spec (rhs val2 val3) - (provide (= result (store_effect #x0000 (widthof val2) val2 #x0000000000000000)))) -(decl rhs (Value Value) Inst) -(extern constructor rhs rhs) - -(rule (lhs true val2 val3) - (rhs val2 val3)) diff --git a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_shl.isle b/cranelift/isle/veri/veri_engine/examples/x86/amode_add_shl.isle deleted file mode 100644 index 9c85f60855f3..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_shl.isle +++ /dev/null @@ -1,158 +0,0 @@ -(type Reg (primitive Reg)) -(type Value (primitive Value)) -(type Type (primitive Type)) -(type Inst (primitive Inst)) -(type Unit (primitive Unit)) -(type MemFlagsData (primitive MemFlagsData)) -(type Gpr (primitive Gpr)) -(type Imm64 (primitive Imm64)) - -(type MInst (enum)) - -(extern const $I64 Type) - -(type ExtendKind (enum Sign Zero)) - -(spec (iconst arg) (provide (= arg (zero_ext 64 result)))) -(decl iconst (Imm64) Inst) -(extern extractor iconst iconst) - -;; fn uimm8(&mut self, x: Imm64) -> Option { -;; let x64: i64 = x.into(); -;; let x8: u8 = x64.try_into().ok()?; -;; Some(x8) -;; } -(spec (uimm8 arg) - (provide (= result (zero_ext 64 arg))) - (require (bvslt result #x0000000000000100) - (= (widthof arg) 8))) -(decl uimm8 (u8) Imm64) -(extern extractor uimm8 uimm8) - -(spec (u8_as_u32 arg) - (provide (= result (zero_ext 32 arg))) - (require - (= (widthof arg) 8) - (= (widthof result) 32))) -(decl pure u8_as_u32 (u8) u32) -(extern constructor u8_as_u32 u8_as_u32) - -(spec (def_inst arg) (provide (= result arg))) -(decl def_inst (Inst) Value) -(extern extractor def_inst def_inst) -(convert Inst Value def_inst) - -(spec (put_in_reg arg) (provide (= result (conv_to 62 arg)))) -(decl put_in_reg (Value) Reg) -(extern constructor put_in_reg put_in_reg) -(convert Value Reg put_in_reg) - -(spec (gpr_to_reg arg) (provide (= result arg))) -(decl gpr_to_reg (Gpr) Reg) -(extern constructor gpr_to_reg gpr_to_reg) -(convert Gpr Reg gpr_to_reg) - -(spec (gpr_new arg) (provide (= result arg))) -(decl gpr_new (Reg) Gpr) -(extern constructor gpr_new gpr_new) -(convert Reg Gpr gpr_new) - -;; To make this case study specific to Wasm, constrain to 32 or 64 -(spec (uextend arg) - (provide (= result (zero_ext (widthof result) arg))) - (require - (or (= (widthof result) 32) (= (widthof result) 64)) - (or (= (widthof result) 32) (= (widthof result) 64)) - (<= (widthof arg) (widthof result)))) -(decl uextend (Value) Inst) -(extern extractor uextend uextend) - -;; fn shift_mask(&mut self, ty: Type) -> ImmLogic { -;; let mask = (ty.lane_bits() - 1) as u64; -;; ImmLogic::maybe_from_u64(mask, I32).unwrap() -;; } -(spec (ishl x y) - (provide - (= result - (bvshl x - (bvand (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) - #x0000000000000001)) - y))))) -(decl ishl (Value Value) Inst) -(extern extractor ishl ishl) - -;; NOTE: partial spec: ignoring the `flags` argument -;; NOTE: to get an easier counterexample, set base to 0 -;; Immediate sign-extended and a register -(spec (Amode.ImmReg simm base flags) - (provide (= result (bvadd base (sign_ext 64 simm)))) - (require - (= (widthof simm) 32) - (= (widthof base) 64) - (= base #x0000000000000000) - (= (widthof flags) 4))) - -;; NOTE: partial spec: ignoring the `flags` argument -;; Sign-extend-32-to-64(simm32) + base + (index << shift) -(spec (Amode.ImmRegRegShift simm base index shift flags) - (provide - (= result - (bvadd - (bvadd base (sign_ext 64 simm)) - (bvshl index (zero_ext 64 shift))))) - (require - (= (widthof simm) 32) - (= (widthof base) 64) - (= flags flags))) - -;; An `Amode` represents a possible addressing mode that can be used -;; in instructions. These denote a 64-bit value only. -(type Amode (enum - ;; Immediate sign-extended and a register - (ImmReg (simm32 u32) - (base Reg) - (flags MemFlagsData)) - - ;; Sign-extend-32-to-64(simm32) + base + (index << shift) - (ImmRegRegShift (simm32 u32) - (base Gpr) - (index Gpr) - (shift u8) - (flags MemFlagsData)) - ) -) - -(spec (amode_add x y) (provide (= result (bvadd x (zero_ext 64 y))))) -(form - amode - ((args (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 64))) -) -(instantiate amode_add amode) -(decl amode_add (Amode Value) Amode) -(extern extractor amode_add amode_add) - -(spec (valid_reg arg) (provide (= result arg))) -(decl valid_reg (Reg) Reg) -(extern extractor valid_reg valid_reg) - -(spec (u32_lteq a b) - (provide (= result ())) - (require (<= a b) - (= (widthof a) 32) - (= (widthof b) 32))) -(decl pure u32_lteq (u32 u32) Unit) -(extern constructor u32_lteq u32_lteq) - -(spec (ExtendKind.Zero) (provide (= result #x0000000000000000))) -(spec (ExtendKind.Sign) (provide (= result #x0000000000000001))) - -(spec (put_in_gpr arg) (provide (= result (conv_to 64 arg)))) -(decl put_in_gpr (Value) Gpr) -(extern constructor put_in_gpr put_in_gpr) -(convert Value Gpr put_in_gpr) - -;; The problematic rule itself -(rule 2 (amode_add (Amode.ImmReg off (valid_reg base) flags) (ishl index (iconst (uimm8 shift)))) - (if (u32_lteq (u8_as_u32 shift) 3)) - (Amode.ImmRegRegShift off base index shift flags)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_uextend_shl.isle b/cranelift/isle/veri/veri_engine/examples/x86/amode_add_uextend_shl.isle deleted file mode 100644 index a3e9ac7451c5..000000000000 --- a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_uextend_shl.isle +++ /dev/null @@ -1,174 +0,0 @@ -;; We need to redefine some primitive ISLE types for this case study, since the source code -;; has changed to remove this bug on the current commit. -(type Reg (primitive Reg)) -(type Value (primitive Value)) -(type Type (primitive Type)) -(type Inst (primitive Inst)) -(type Unit (primitive Unit)) -(type MemFlagsData (primitive MemFlagsData)) -(type Gpr (primitive Gpr)) -(type Imm64 (primitive Imm64)) - -(type MInst (enum)) - -(extern const $I64 Type) - -(type ExtendKind (enum Sign Zero)) - -;; An Crocus specification; potentially-narrow IR values are zero-extended to 64 bits -(spec (iconst arg) (provide (= arg (zero_ext 64 result)))) -(decl iconst (Imm64) Inst) -(extern extractor iconst iconst) - -;; An Crocus specification modeling the following logic: -;; fn uimm8(&mut self, x: Imm64) -> Option { -;; let x64: i64 = x.into(); -;; let x8: u8 = x64.try_into().ok()?; -;; Some(x8) -;; } -(spec (uimm8 arg) - (provide (= result (zero_ext 64 arg))) - (require (bvslt result #x0000000000000100) - (= (widthof arg) 8))) -(decl uimm8 (u8) Imm64) -(extern extractor uimm8 uimm8) - -(spec (u8_as_u32 arg) - (provide (= result (zero_ext 32 arg))) - (require - (= (widthof arg) 8) - (= (widthof result) 32))) -(decl pure u8_as_u32 (u8) u32) -(extern constructor u8_as_u32 u8_as_u32) - -(spec (def_inst arg) (provide (= result arg))) -(decl def_inst (Inst) Value) -(extern extractor def_inst def_inst) -(convert Inst Value def_inst) - -(spec (put_in_reg arg) (provide (= result (conv_to 62 arg)))) -(decl put_in_reg (Value) Reg) -(extern constructor put_in_reg put_in_reg) -(convert Value Reg put_in_reg) - -(spec (gpr_to_reg arg) (provide (= result arg))) -(decl gpr_to_reg (Gpr) Reg) -(extern constructor gpr_to_reg gpr_to_reg) -(convert Gpr Reg gpr_to_reg) - -(spec (gpr_new arg) (provide (= result arg))) -(decl gpr_new (Reg) Gpr) -(extern constructor gpr_new gpr_new) -(convert Reg Gpr gpr_new) - -;; To make this case study specific to Wasm, constrain the widths of -;; the argument and returned value to 32 or 64 -(spec (uextend arg) - (provide (= result (zero_ext (widthof result) arg))) - (require - (or (= (widthof arg) 32) (= (widthof arg) 64)) - (or (= (widthof result) 32) (= (widthof result) 64)) - (<= (widthof arg) (widthof result)))) -(decl uextend (Value) Inst) -(extern extractor uextend uextend) - -;; Crocus specification to model the Wasm shift semantics: -;; fn shift_mask(&mut self, ty: Type) -> ImmLogic { -;; let mask = (ty.lane_bits() - 1) as u64; -;; ImmLogic::maybe_from_u64(mask, I32).unwrap() -;; } -;; NOTE: restricted to Wasm types for this case study -(spec (ishl x y) - (provide - (= result - (bvshl x - (bvand (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) - #x0000000000000001)) - y)))) - (require - (or (= (widthof x) 32) (= (widthof x) 64)) - (or (= (widthof y) 32) (= (widthof y) 64)))) -(decl ishl (Value Value) Inst) -(extern extractor ishl ishl) - -;; NOTE: partial spec: ignoring the `flags` argument -;; NOTE: to get an easier counterexample, set base to 0 -;; Immediate sign-extended and a register -(spec (Amode.ImmReg simm base flags) - (provide (= result (bvadd base (sign_ext 64 simm)))) - (require - (= (widthof simm) 32) - (= (widthof base) 64) - (= base #x0000000000000000) - (= (widthof flags) 4))) - -;; NOTE: partial spec: ignoring the `flags` argument -;; Sign-extend-32-to-64(simm32) + base + (index << shift) -(spec (Amode.ImmRegRegShift simm base index shift flags) - (provide - (= result - (bvadd - (bvadd base (sign_ext 64 simm)) - (bvshl index (zero_ext 64 shift))))) - (require - (= (widthof simm) 32) - (= (widthof base) 64) - (= (widthof base) 64) - (= flags flags))) - -;; An `Amode` represents a possible addressing mode that can be used -;; in instructions. These denote a 64-bit value only. -(type Amode (enum - ;; Immediate sign-extended and a register - (ImmReg (simm32 u32) - (base Reg) - (flags MemFlagsData)) - - ;; Sign-extend-32-to-64(simm32) + base + (index << shift) - (ImmRegRegShift (simm32 u32) - (base Gpr) - (index Gpr) - (shift u8) - (flags MemFlagsData)) - ) -) - -(spec (amode_add x y) (provide (= result (bvadd x (zero_ext 64 y))))) -(form - amode - ((args (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 32))) - ((args (bv 64) (bv 32)) (ret (bv 64)) (canon (bv 64))) -) -(instantiate amode_add amode) -(decl amode_add (Amode Value) Amode) -(extern extractor amode_add amode_add) - -(spec (valid_reg arg) (provide (= result arg))) -(decl valid_reg (Reg) Reg) -(extern extractor valid_reg valid_reg) - -(spec (u32_lteq a b) - (provide (= result ())) - (require (<= a b) - (= (widthof a) 32) - (= (widthof b) 32))) -(decl pure u32_lteq (u32 u32) Unit) -(extern constructor u32_lteq u32_lteq) - -(spec (ExtendKind.Zero) (provide (= result #x0000000000000000))) -(spec (ExtendKind.Sign) (provide (= result #x0000000000000001))) - -(spec (extend_to_gpr v ty ext) - (provide - (= result - (if (= ext #x0000000000000000) - (zero_ext ty v) - (sign_ext ty v))))) -(decl extend_to_gpr (Value Type ExtendKind) Gpr) -(extern constructor extend_to_gpr extend_to_gpr) - -;; The problematic rule itself -(rule 2 (amode_add (Amode.ImmReg off (valid_reg base) flags) - (uextend (ishl index (iconst (uimm8 shift))))) - (if (u32_lteq (u8_as_u32 shift) 3)) - (Amode.ImmRegRegShift off base (extend_to_gpr index $I64 (ExtendKind.Zero)) shift flags)) \ No newline at end of file diff --git a/cranelift/isle/veri/veri_engine/src/annotations.rs b/cranelift/isle/veri/veri_engine/src/annotations.rs deleted file mode 100644 index f7a70886aced..000000000000 --- a/cranelift/isle/veri/veri_engine/src/annotations.rs +++ /dev/null @@ -1,465 +0,0 @@ -use cranelift_isle::ast::{self, Signature}; -use std::collections::HashMap; -use veri_ir::annotation_ir; - -use cranelift_isle::ast::{Def, Ident, Model, ModelType, SpecExpr, SpecOp}; -use cranelift_isle::lexer::Pos; -use cranelift_isle::sema::{TermEnv, TermId, TypeEnv, TypeId}; -use veri_ir::annotation_ir::Width; -use veri_ir::annotation_ir::{BoundVar, Const, Expr, TermAnnotation, TermSignature, Type}; -use veri_ir::TermSignature as TermTypeSignature; - -static RESULT: &str = "result"; - -#[derive(Clone, Debug)] -pub struct ParsingEnv<'a> { - pub typeenv: &'a TypeEnv, - pub enums: HashMap, -} - -#[derive(Clone, Debug)] -pub struct AnnotationEnv { - pub annotation_map: HashMap, - - // Mapping from ISLE term to its signature instantiations. - pub instantiations_map: HashMap>, - - // Mapping from ISLE type to its model (the annotation used to represent - // it). - pub model_map: HashMap, -} - -impl AnnotationEnv { - pub fn get_annotation_for_term(&self, term_id: &TermId) -> Option { - if self.annotation_map.contains_key(term_id) { - return Some(self.annotation_map[term_id].clone()); - } - None - } - - pub fn get_term_signatures_by_name( - &self, - termenv: &TermEnv, - typeenv: &TypeEnv, - ) -> HashMap> { - let mut term_signatures_by_name = HashMap::new(); - for (term_id, term_sigs) in &self.instantiations_map { - let sym = termenv.terms[term_id.index()].name; - let name = typeenv.syms[sym.index()].clone(); - term_signatures_by_name.insert(name, term_sigs.clone()); - } - term_signatures_by_name - } -} - -pub fn spec_to_annotation_bound_var(i: &Ident) -> BoundVar { - BoundVar { - name: i.0.clone(), - ty: None, - } -} - -fn spec_to_usize(s: &SpecExpr) -> Option { - match s { - SpecExpr::ConstInt { val, pos: _ } => Some(*val as usize), - _ => None, - } -} - -fn spec_op_to_expr(s: &SpecOp, args: &[SpecExpr], pos: &Pos, env: &ParsingEnv) -> Expr { - fn unop) -> Expr>( - u: F, - args: &[SpecExpr], - pos: &Pos, - env: &ParsingEnv, - ) -> Expr { - assert_eq!( - args.len(), - 1, - "Unexpected number of args for unary operator {pos:?}", - ); - u(Box::new(spec_to_expr(&args[0], env))) - } - fn binop, Box) -> Expr>( - b: F, - args: &[SpecExpr], - _pos: &Pos, - env: &ParsingEnv, - ) -> Expr { - assert_eq!( - args.len(), - 2, - "Unexpected number of args for binary operator {args:?}", - ); - b( - Box::new(spec_to_expr(&args[0], env)), - Box::new(spec_to_expr(&args[1], env)), - ) - } - - fn variadic_binop, Box) -> Expr>( - b: F, - args: &[SpecExpr], - pos: &Pos, - env: &ParsingEnv, - ) -> Expr { - assert!( - !args.is_empty(), - "Unexpected number of args for variadic binary operator {pos:?}", - ); - let mut expr_args: Vec = args.iter().map(|a| spec_to_expr(a, env)).collect(); - let last = expr_args.remove(expr_args.len() - 1); - - // Reverse to keep the order of the original list - expr_args - .iter() - .rev() - .fold(last, |acc, a| b(Box::new(a.clone()), Box::new(acc))) - } - - match s { - // Unary - SpecOp::Not => unop(Expr::Not, args, pos, env), - SpecOp::BVNot => unop(Expr::BVNot, args, pos, env), - SpecOp::BVNeg => unop(Expr::BVNeg, args, pos, env), - SpecOp::Rev => unop(Expr::Rev, args, pos, env), - SpecOp::Clz => unop(Expr::CLZ, args, pos, env), - SpecOp::Cls => unop(Expr::CLS, args, pos, env), - SpecOp::Popcnt => unop(Expr::BVPopcnt, args, pos, env), - SpecOp::BV2Int => unop(Expr::BVToInt, args, pos, env), - - // Variadic binops - SpecOp::And => variadic_binop(Expr::And, args, pos, env), - SpecOp::Or => variadic_binop(Expr::Or, args, pos, env), - - // Binary - SpecOp::Eq => binop(Expr::Eq, args, pos, env), - SpecOp::Lt => binop(Expr::Lt, args, pos, env), - SpecOp::Lte => binop(Expr::Lte, args, pos, env), - SpecOp::Gt => binop(|x, y| Expr::Lt(y, x), args, pos, env), - SpecOp::Gte => binop(|x, y| Expr::Lte(y, x), args, pos, env), - SpecOp::Imp => binop(Expr::Imp, args, pos, env), - SpecOp::BVAnd => binop(Expr::BVAnd, args, pos, env), - SpecOp::BVOr => binop(Expr::BVOr, args, pos, env), - SpecOp::BVXor => binop(Expr::BVXor, args, pos, env), - SpecOp::BVAdd => binop(Expr::BVAdd, args, pos, env), - SpecOp::BVSub => binop(Expr::BVSub, args, pos, env), - SpecOp::BVMul => binop(Expr::BVMul, args, pos, env), - SpecOp::BVUdiv => binop(Expr::BVUDiv, args, pos, env), - SpecOp::BVUrem => binop(Expr::BVUrem, args, pos, env), - SpecOp::BVSdiv => binop(Expr::BVSDiv, args, pos, env), - SpecOp::BVSrem => binop(Expr::BVSrem, args, pos, env), - SpecOp::BVShl => binop(Expr::BVShl, args, pos, env), - SpecOp::BVLshr => binop(Expr::BVShr, args, pos, env), - SpecOp::BVAshr => binop(Expr::BVAShr, args, pos, env), - SpecOp::BVSaddo => binop(Expr::BVSaddo, args, pos, env), - SpecOp::BVUle => binop(Expr::BVUlte, args, pos, env), - SpecOp::BVUlt => binop(Expr::BVUlt, args, pos, env), - SpecOp::BVUgt => binop(Expr::BVUgt, args, pos, env), - SpecOp::BVUge => binop(Expr::BVUgte, args, pos, env), - SpecOp::BVSlt => binop(Expr::BVSlt, args, pos, env), - SpecOp::BVSle => binop(Expr::BVSlte, args, pos, env), - SpecOp::BVSgt => binop(Expr::BVSgt, args, pos, env), - SpecOp::BVSge => binop(Expr::BVSgte, args, pos, env), - SpecOp::Rotr => binop(Expr::BVRotr, args, pos, env), - SpecOp::Rotl => binop(Expr::BVRotl, args, pos, env), - SpecOp::ZeroExt => match spec_to_usize(&args[0]) { - Some(i) => Expr::BVZeroExtTo( - Box::new(Width::Const(i)), - Box::new(spec_to_expr(&args[1], env)), - ), - None => binop(Expr::BVZeroExtToVarWidth, args, pos, env), - }, - SpecOp::SignExt => match spec_to_usize(&args[0]) { - Some(i) => Expr::BVSignExtTo( - Box::new(Width::Const(i)), - Box::new(spec_to_expr(&args[1], env)), - ), - None => binop(Expr::BVSignExtToVarWidth, args, pos, env), - }, - SpecOp::ConvTo => binop(Expr::BVConvTo, args, pos, env), - SpecOp::Concat => { - let cases: Vec = args.iter().map(|a| spec_to_expr(a, env)).collect(); - Expr::BVConcat(cases) - } - SpecOp::Extract => { - assert_eq!( - args.len(), - 3, - "Unexpected number of args for extract operator {pos:?}", - ); - Expr::BVExtract( - spec_to_usize(&args[0]).unwrap(), - spec_to_usize(&args[1]).unwrap(), - Box::new(spec_to_expr(&args[2], env)), - ) - } - SpecOp::Int2BV => { - assert_eq!( - args.len(), - 2, - "Unexpected number of args for Int2BV operator {pos:?}", - ); - Expr::BVIntToBv( - spec_to_usize(&args[0]).unwrap(), - Box::new(spec_to_expr(&args[1], env)), - ) - } - SpecOp::Subs => { - assert_eq!( - args.len(), - 3, - "Unexpected number of args for subs operator {pos:?}", - ); - Expr::BVSubs( - Box::new(spec_to_expr(&args[0], env)), - Box::new(spec_to_expr(&args[1], env)), - Box::new(spec_to_expr(&args[2], env)), - ) - } - SpecOp::WidthOf => unop(Expr::WidthOf, args, pos, env), - SpecOp::If => { - assert_eq!( - args.len(), - 3, - "Unexpected number of args for extract operator {pos:?}", - ); - Expr::Conditional( - Box::new(spec_to_expr(&args[0], env)), - Box::new(spec_to_expr(&args[1], env)), - Box::new(spec_to_expr(&args[2], env)), - ) - } - SpecOp::Switch => { - assert!( - args.len() > 1, - "Unexpected number of args for switch operator {pos:?}", - ); - let switch_on = spec_to_expr(&args[0], env); - let arms: Vec<(Expr, Expr)> = args[1..] - .iter() - .map(|a| match a { - SpecExpr::Pair { l, r } => { - let l_expr = spec_to_expr(l, env); - let r_expr = spec_to_expr(r, env); - (l_expr, r_expr) - } - _ => unreachable!(), - }) - .collect(); - Expr::Switch(Box::new(switch_on), arms) - } - SpecOp::LoadEffect => { - assert_eq!( - args.len(), - 3, - "Unexpected number of args for load operator {pos:?}", - ); - Expr::LoadEffect( - Box::new(spec_to_expr(&args[0], env)), - Box::new(spec_to_expr(&args[1], env)), - Box::new(spec_to_expr(&args[2], env)), - ) - } - SpecOp::StoreEffect => { - assert_eq!( - args.len(), - 4, - "Unexpected number of args for store operator {pos:?}", - ); - Expr::StoreEffect( - Box::new(spec_to_expr(&args[0], env)), - Box::new(spec_to_expr(&args[1], env)), - Box::new(spec_to_expr(&args[2], env)), - Box::new(spec_to_expr(&args[3], env)), - ) - } - } -} - -fn spec_to_expr(s: &SpecExpr, env: &ParsingEnv) -> Expr { - match s { - SpecExpr::ConstUnit { pos: _ } => Expr::Const(Const { - ty: Type::Unit, - value: 0, - width: 0, - }), - SpecExpr::ConstInt { val, pos: _ } => Expr::Const(Const { - ty: Type::Int, - value: *val, - width: 0, - }), - SpecExpr::ConstBitVec { val, width, pos: _ } => Expr::Const(Const { - ty: Type::BitVectorWithWidth(*width as usize), - value: *val, - width: (*width as usize), - }), - SpecExpr::ConstBool { val, pos: _ } => Expr::Const(Const { - ty: Type::Bool, - value: *val as i128, - width: 0, - }), - SpecExpr::Var { var, pos: _ } => Expr::Var(var.0.clone()), - SpecExpr::Op { op, args, pos } => spec_op_to_expr(op, args, pos, env), - SpecExpr::Pair { l, r } => { - unreachable!("pairs currently only parsed as part of Switch statements, {l:?} {r:?}",) - } - SpecExpr::Enum { name } => { - if let Some(e) = env.enums.get(&name.0) { - e.clone() - } else { - panic!("Can't find model for enum {}", name.0); - } - } - } -} - -fn model_type_to_type(model_type: &ModelType) -> veri_ir::Type { - match model_type { - ModelType::Int => veri_ir::Type::Int, - ModelType::Unit => veri_ir::Type::Unit, - ModelType::Bool => veri_ir::Type::Bool, - ModelType::BitVec(size) => veri_ir::Type::BitVector(*size), - } -} - -fn signature_to_term_type_signature(sig: &Signature) -> TermTypeSignature { - TermTypeSignature { - args: sig.args.iter().map(model_type_to_type).collect(), - ret: model_type_to_type(&sig.ret), - canonical_type: Some(model_type_to_type(&sig.canonical)), - } -} - -pub fn parse_annotations(defs: &[Def], termenv: &TermEnv, typeenv: &TypeEnv) -> AnnotationEnv { - let mut annotation_map = HashMap::new(); - let mut model_map = HashMap::new(); - - let mut env = ParsingEnv { - typeenv, - enums: HashMap::new(), - }; - - // Traverse models to process spec annotations for enums - for def in defs { - if let &ast::Def::Model(Model { ref name, ref val }) = def { - match val { - ast::ModelValue::TypeValue(model_type) => { - let type_id = typeenv.get_type_by_name(name).unwrap(); - let ir_type = match model_type { - ModelType::Int => annotation_ir::Type::Int, - ModelType::Unit => annotation_ir::Type::Unit, - ModelType::Bool => annotation_ir::Type::Bool, - ModelType::BitVec(None) => annotation_ir::Type::BitVector, - ModelType::BitVec(Some(size)) => { - annotation_ir::Type::BitVectorWithWidth(*size) - } - }; - model_map.insert(type_id, ir_type); - } - ast::ModelValue::EnumValues(vals) => { - for (v, e) in vals { - let ident = ast::Ident(format!("{}.{}", name.0, v.0), v.1); - let term_id = termenv.get_term_by_name(typeenv, &ident).unwrap(); - let val = spec_to_expr(e, &env); - let ty = match val { - Expr::Const(Const { ref ty, .. }) => ty, - _ => unreachable!(), - }; - env.enums.insert(ident.0.clone(), val.clone()); - let result = BoundVar { - name: RESULT.to_string(), - ty: Some(ty.clone()), - }; - let sig = TermSignature { - args: vec![], - ret: result, - }; - let annotation = TermAnnotation { - sig, - assumptions: vec![Box::new(Expr::Eq( - Box::new(Expr::Var(RESULT.to_string())), - Box::new(val), - ))], - assertions: vec![], - }; - annotation_map.insert(term_id, annotation); - } - } - } - } - } - - // Traverse defs to process spec annotations - for def in defs { - if let ast::Def::Spec(spec) = def { - let termname = spec.term.0.clone(); - let term_id = termenv - .get_term_by_name(typeenv, &spec.term) - .unwrap_or_else(|| panic!("Spec provided for unknown decl {termname}")); - assert!( - !annotation_map.contains_key(&term_id), - "duplicate spec for {termname}", - ); - let sig = TermSignature { - args: spec.args.iter().map(spec_to_annotation_bound_var).collect(), - ret: BoundVar { - name: RESULT.to_string(), - ty: None, - }, - }; - - let mut assumptions = vec![]; - let mut assertions = vec![]; - for a in &spec.provides { - assumptions.push(Box::new(spec_to_expr(a, &env))); - } - - for a in &spec.requires { - assertions.push(Box::new(spec_to_expr(a, &env))); - } - - let annotation = TermAnnotation { - sig, - assumptions, - assertions, - }; - annotation_map.insert(term_id, annotation); - } - } - - // Collect term instantiations. - let mut forms_map = HashMap::new(); - for def in defs { - if let ast::Def::Form(form) = def { - let term_type_signatures: Vec<_> = form - .signatures - .iter() - .map(signature_to_term_type_signature) - .collect(); - forms_map.insert(form.name.0.clone(), term_type_signatures); - } - } - - let mut instantiations_map = HashMap::new(); - for def in defs { - if let ast::Def::Instantiation(inst) = def { - let term_id = termenv.get_term_by_name(typeenv, &inst.term).unwrap(); - let sigs = match &inst.form { - Some(form) => forms_map[&form.0].clone(), - None => inst - .signatures - .iter() - .map(signature_to_term_type_signature) - .collect(), - }; - instantiations_map.insert(term_id, sigs); - } - } - - AnnotationEnv { - annotation_map, - instantiations_map, - model_map, - } -} diff --git a/cranelift/isle/veri/veri_engine/src/interp.rs b/cranelift/isle/veri/veri_engine/src/interp.rs deleted file mode 100644 index 35aaafcb4b9a..000000000000 --- a/cranelift/isle/veri/veri_engine/src/interp.rs +++ /dev/null @@ -1,50 +0,0 @@ -/// Interpret and build an assumption context from the LHS and RHS of rules. -use crate::type_inference::RuleSemantics; -use veri_ir::{BoundVar, Expr}; - -use std::collections::HashMap; -use std::fmt::Debug; - -use cranelift_isle as isle; -use isle::sema::{RuleId, VarId}; - -/// Assumption consist of single verification IR expressions, which must have -/// boolean type. -#[derive(Clone, Debug)] -pub struct Assumption { - assume: Expr, -} - -impl Assumption { - /// Create a new assumption, checking type. - pub fn new(assume: Expr) -> Self { - // assert!(assume.ty().is_bool()); - Self { assume } - } - - /// Get the assumption as an expression. - pub fn assume(&self) -> &Expr { - &self.assume - } -} -pub struct Context<'ctx> { - pub quantified_vars: Vec, - pub free_vars: Vec, - pub assumptions: Vec, - pub var_map: HashMap, - - // For type checking - pub typesols: &'ctx HashMap, -} - -impl<'ctx> Context<'ctx> { - pub fn new(typesols: &'ctx HashMap) -> Context<'ctx> { - Context { - quantified_vars: vec![], - free_vars: vec![], - assumptions: vec![], - var_map: HashMap::new(), - typesols, - } - } -} diff --git a/cranelift/isle/veri/veri_engine/src/lib.rs b/cranelift/isle/veri/veri_engine/src/lib.rs deleted file mode 100644 index 7314839fe1e6..000000000000 --- a/cranelift/isle/veri/veri_engine/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -use easy_smt::SExpr; - -pub mod annotations; -pub mod interp; -pub mod solver; -pub mod termname; -pub mod type_inference; -pub mod verify; - -pub const REG_WIDTH: usize = 64; - -// Use a distinct with as the maximum width any value should have within type inference -pub const MAX_WIDTH: usize = 2 * REG_WIDTH; - -pub const FLAGS_WIDTH: usize = 4; - -pub const WIDTHS: [usize; 4] = [8, 16, 32, 64]; - -// Closure arguments: SMT context, arguments to the term, lhs, rhs -type CustomCondition = dyn Fn(&easy_smt::Context, Vec, SExpr, SExpr) -> SExpr; - -// Closure arguments: SMT context, arguments to the term -type CustomAssumption = dyn Fn(&easy_smt::Context, Vec) -> SExpr; - -pub struct Config { - pub term: String, - pub names: Option>, - pub distinct_check: bool, - - pub custom_verification_condition: Option>, - pub custom_assumptions: Option>, -} - -impl Config { - pub fn with_term_and_name(term: &str, name: &str) -> Self { - Config { - term: term.to_string(), - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: None, - names: Some(vec![name.to_string()]), - } - } -} diff --git a/cranelift/isle/veri/veri_engine/src/main.rs b/cranelift/isle/veri/veri_engine/src/main.rs deleted file mode 100644 index a362fec56858..000000000000 --- a/cranelift/isle/veri/veri_engine/src/main.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! Prototype verification tool for Cranelift's ISLE lowering rules. - -use clap::{ArgAction, Parser}; -use cranelift_codegen_meta::{generate_isle, isle::get_isle_compilations}; -use std::path::PathBuf; -use std::{env, fs}; -use veri_engine_lib::verify::verify_rules; -use veri_engine_lib::Config; - -#[derive(Parser)] -#[clap(about, version, author)] -struct Args { - /// Path to codegen crate directory. - #[clap(long, required = true)] - codegen: std::path::PathBuf, - - /// Sets the input file - #[clap(short, long)] - input: Option, - - /// Which LHS root to verify - #[clap(short, long, default_value = "lower")] - term: String, - - /// Which width types to verify - #[clap(long)] - widths: Option>, - - /// Which named rule to verify - #[clap(long)] - names: Option>, - - /// Don't use the prelude ISLE files - #[clap(short, long, action=ArgAction::SetTrue)] - noprelude: bool, - - /// Include the aarch64 files - #[clap(short, long, action=ArgAction::SetTrue)] - aarch64: bool, - - /// Include the x64 files - #[clap(short, long, action=ArgAction::SetTrue)] - x64: bool, - - /// Don't check for distinct possible models - #[clap(long, action=ArgAction::SetTrue)] - nodistinct: bool, -} - -impl Args { - fn isle_input_files(&self) -> anyhow::Result> { - // Generate ISLE files. - let cur_dir = env::current_dir().expect("Can't access current working directory"); - let gen_dir = cur_dir.join("output"); - if !std::path::Path::new(gen_dir.as_path()).exists() { - fs::create_dir_all(gen_dir.as_path()).unwrap(); - } - generate_isle(gen_dir.as_path()).expect("Can't generate ISLE"); - - let inst_specs_isle = self.codegen.join("src").join("inst_specs.isle"); - - // Lookup ISLE compilations. - let compilations = get_isle_compilations(&self.codegen, gen_dir.as_path()); - - let name = match (self.aarch64, self.x64) { - (true, false) => "aarch64", - (false, true) => "x64", - _ => panic!("aarch64 of x64 backend must be provided"), - }; - - let mut inputs = compilations - .lookup(name) - .ok_or(anyhow::format_err!("unknown ISLE compilation: {}", name))? - .inputs(); - inputs.push(inst_specs_isle); - - // Return inputs from the matching compilation, if any. - Ok(inputs) - } -} - -fn main() -> anyhow::Result<()> { - env_logger::init(); - let args = Args::parse(); - - let valid_widths = ["I8", "I16", "I32", "I64"]; - if let Some(widths) = &args.widths { - for w in widths { - let w_str = w.as_str(); - if !valid_widths.contains(&w_str) { - panic!("Invalid width type: {w}"); - } - } - } - - let inputs = if args.noprelude { - vec![PathBuf::from( - args.input.expect("Missing input file in noprelude mode"), - )] - } else { - args.isle_input_files()? - }; - - let names = if let Some(names) = args.names { - let mut names = names; - names.sort(); - names.dedup(); - Some(names) - } else { - None - }; - - let config = Config { - term: args.term, - names, - distinct_check: !args.nodistinct, - custom_verification_condition: None, - custom_assumptions: None, - }; - - verify_rules(inputs, &config, &args.widths) - .map_err(|e| anyhow::anyhow!("failed to compile ISLE: {:?}", e)) -} diff --git a/cranelift/isle/veri/veri_engine/src/solver.rs b/cranelift/isle/veri/veri_engine/src/solver.rs deleted file mode 100644 index 39dcb9df4a3f..000000000000 --- a/cranelift/isle/veri/veri_engine/src/solver.rs +++ /dev/null @@ -1,2120 +0,0 @@ -/// Convert our internal Verification IR to an external SMT AST and pass -/// queries to that solver. -/// -/// This uses the easy-smt crate to interact with any solver. -/// -use cranelift_isle as isle; -use isle::sema::{Pattern, Rule, TermEnv, TypeEnv}; - -use crate::solver::encoded_ops::popcnt::popcnt; -use crate::type_inference::RuleSemantics; -use crate::Config; -use easy_smt::{Response, SExpr}; -use std::cmp::Ordering; -use std::collections::HashMap; -use veri_ir::{ - BinaryOp, ConcreteTest, Counterexample, Expr, TermSignature, Terminal, Type, TypeContext, - UnaryOp, VerificationResult, -}; - -mod encoded_ops; - -use encoded_ops::cls; -use encoded_ops::clz; -use encoded_ops::rev; -use encoded_ops::subs; - -use crate::MAX_WIDTH; - -pub struct SolverCtx { - smt: easy_smt::Context, - pub find_widths: bool, - tyctx: TypeContext, - pub bitwidth: usize, - var_map: HashMap, - width_vars: HashMap, - width_assumptions: Vec, - pub additional_decls: Vec<(String, SExpr)>, - pub additional_assumptions: Vec, - pub additional_assertions: Vec, - fresh_bits_idx: usize, - lhs_load_args: Option>, - rhs_load_args: Option>, - lhs_store_args: Option>, - rhs_store_args: Option>, - load_return: Option, - lhs_flag: bool, -} - -pub struct RuleCtx<'a> { - rule_sem: &'a RuleSemantics, - rule: &'a Rule, - termenv: &'a TermEnv, - typeenv: &'a TypeEnv, - config: &'a Config, -} - -impl SolverCtx { - pub fn new_fresh_bits(&mut self, width: usize) -> SExpr { - let name = format!("fresh{}", self.fresh_bits_idx); - self.fresh_bits_idx += 1; - self.additional_decls - .push((name.clone(), self.smt.bit_vec_sort(self.smt.numeral(width)))); - self.smt.atom(name) - } - - fn new_fresh_int(&mut self) -> SExpr { - let name = format!("fresh{}", self.fresh_bits_idx); - self.fresh_bits_idx += 1; - self.additional_decls - .push((name.clone(), self.smt.int_sort())); - self.smt.atom(name) - } - - fn new_fresh_bool(&mut self) -> SExpr { - let name = format!("fresh{}", self.fresh_bits_idx); - self.fresh_bits_idx += 1; - self.additional_decls - .push((name.clone(), self.smt.bool_sort())); - self.smt.atom(name) - } - - fn declare(&mut self, name: String, typ: SExpr) -> SExpr { - let atom = self.smt.atom(&name); - self.additional_decls.push((name, typ)); - atom - } - - fn assume(&mut self, expr: SExpr) { - self.additional_assumptions.push(expr); - } - - fn assert(&mut self, expr: SExpr) { - self.additional_assertions.push(expr); - } - - /// Construct a constant bit-vector value of the given width. (This is used so pervasively that - /// perhaps we should submit it for inclusion in the easy_smt library...) - fn bv(&self, value: i128, width: usize) -> SExpr { - if value < 0 { - return self - .smt - .list(vec![self.smt.atom("bvneg"), self.bv(-value, width)]); - } - self.smt.list(vec![ - self.smt.atoms().und, - self.smt.atom(format!("bv{value}")), - self.smt.numeral(width), - ]) - } - - /// Convert an SMT integer to a bit vector of a given width. - fn int2bv(&self, width: usize, value: SExpr) -> SExpr { - self.smt.list(vec![ - self.smt.list(vec![ - self.smt.atoms().und, - self.smt.atom("int2bv"), - self.smt.numeral(width), - ]), - value, - ]) - } - - /// Convert an SMT bit vector to a nat. - fn bv2nat(&self, value: SExpr) -> SExpr { - self.smt.list(vec![self.smt.atom("bv2nat"), value]) - } - - /// Zero-extend an SMT bit vector to a wider bit vector by adding `padding` zeroes to the - /// front. - fn zero_extend(&self, padding: usize, value: SExpr) -> SExpr { - if padding == 0 { - return value; - } - self.smt.list(vec![ - self.smt.list(vec![ - self.smt.atoms().und, - self.smt.atom("zero_extend"), - self.smt.numeral(padding), - ]), - value, - ]) - } - - /// Sign-extend an SMT bit vector to a wider bit vector by adding `padding` zeroes to the - /// front. - fn sign_extend(&self, padding: usize, value: SExpr) -> SExpr { - self.smt.list(vec![ - self.smt.list(vec![ - self.smt.atoms().und, - self.smt.atom("sign_extend"), - self.smt.numeral(padding), - ]), - value, - ]) - } - - // Extend with concrete source and destination sizes. Includes extracting relevant bits. - fn extend_concrete( - &mut self, - dest_width: usize, - source: SExpr, - source_width: usize, - op: &str, - ) -> SExpr { - if dest_width < source_width { - log::warn!( - "Unexpected extend widths for {}: dest {dest_width} source {source_width} ", - self.smt.display(source), - ); - self.assert(self.smt.false_()); - return self.bv( - 0, - if self.find_widths { - self.bitwidth - } else { - dest_width - }, - ); - } - - let delta = dest_width - source_width; - if !self.find_widths { - return self.smt.list(vec![ - self.smt.list(vec![ - self.smt.atoms().und, - self.smt.atom(op), - self.smt.numeral(delta), - ]), - source, - ]); - } - - // Extract the relevant bits of the source (which is modeled with a wider, - // register-width bitvector). - let extract = self - .smt - .extract(source_width.wrapping_sub(1).try_into().unwrap(), 0, source); - - // Do the extend itself. - let extend = self.smt.list(vec![ - self.smt.list(vec![ - self.smt.atoms().und, - self.smt.atom(op), - self.smt.numeral(delta), - ]), - extract, - ]); - - // Pad the extended result back to the full register bitwidth. Use the bits - // that were already in the source register. That is, given: - // reg - source width source width - // | | - // SOURCE: [ don't care bits | care bits ] - // - // dest width - // | - // OUT: [ same don't care bits | defined extend | care bits ] - let mut unconstrained_bits = 0; - if dest_width < self.bitwidth { - unconstrained_bits = self - .bitwidth - .checked_sub(delta) - .unwrap() - .checked_sub(source_width) - .unwrap(); - } - - // If we are extending to the full register width, no padding needed - if unconstrained_bits == 0 { - extend - } else { - let padding = self.smt.extract( - self.bitwidth.checked_sub(1).unwrap().try_into().unwrap(), - self.bitwidth - .checked_sub(unconstrained_bits) - .unwrap() - .try_into() - .unwrap(), - source, - ); - self.smt.concat(padding, extend) - } - } - - // SMT-LIB only supports extends (zero or sign) by concrete amounts, but we - // need symbolic ones. This method essentially does if-conversion over possible - // concrete forms, outputting nested ITE blocks. We consider both the starting - // width and the destination width to be potentially symbolic. - // For safety, we add an assertion that some arm of this ITE must match. - fn extend_symbolic( - &mut self, - dest_width: SExpr, - source: SExpr, - source_width: SExpr, - op: &str, - ) -> SExpr { - if self.find_widths { - return source; - } - // Symbolic expression for amount to shift - let shift = self.smt.sub(dest_width, source_width); - - let mut some_match = vec![]; - let mut ite_str = source; - - // Special case: if we are asked to extend by 0, just return the source - let matching = self.smt.eq(self.smt.numeral(0), shift); - some_match.push(matching); - ite_str = self.smt.ite(matching, source, ite_str); - - // Possible amounts to extend by - for possible_delta in 1..self.bitwidth + 1 { - // Possible starting widths - for possible_source in 1..self.bitwidth + 1 { - // For now, ignore extends beyond the bitwidth. This is safe because - // we will fail the rule feasibility check if this is violated. - if possible_source + possible_delta > self.bitwidth { - continue; - } - - // Statement meaning the symbolic case matches this concrete case - let matching = self.smt.and( - self.smt.eq(self.smt.numeral(possible_delta), shift), - self.smt.eq(self.smt.numeral(possible_source), source_width), - ); - some_match.push(matching); - let extend = self.extend_concrete( - possible_source + possible_delta, - source, - possible_source, - op, - ); - ite_str = self.smt.ite(matching, extend, ite_str); - } - } - let some_shift_matches = self.smt.or_many(some_match); - self.width_assumptions.push(some_shift_matches); - ite_str - } - - fn encode_rotate(&self, op: &str, source: SExpr, amount: SExpr, width: usize) -> SExpr { - // SMT bitvector rotate_left requires that the rotate amount be - // statically specified. Instead, to use a dynamic amount, desugar - // to shifts and bit arithmetic. - let width_as_bv = self.bv(width.try_into().unwrap(), width); - let wrapped_amount = self.smt.bvurem(amount, width_as_bv); - let wrapped_delta = self.smt.bvsub(width_as_bv, wrapped_amount); - match op { - "rotate_left" => self.smt.bvor( - self.smt.bvshl(source, wrapped_amount), - self.smt.bvlshr(source, wrapped_delta), - ), - "rotate_right" => self.smt.bvor( - self.smt.bvshl(source, wrapped_delta), - self.smt.bvlshr(source, wrapped_amount), - ), - _ => unreachable!(), - } - } - - // SMT bitvector rotate requires that the rotate amount be - // statically specified. Instead, to use a dynamic amount, desugar - // to shifts and bit arithmetic. - fn rotate_symbolic( - &mut self, - source: SExpr, - source_width: usize, - amount: SExpr, - op: &str, - ) -> SExpr { - if self.find_widths { - return source; - } - let (s, a) = if self.find_widths { - // Extract the relevant bits of the source (which is modeled with a wider, - // register-width bitvector). - let extract_source = self.smt.extract( - source_width.checked_sub(1).unwrap().try_into().unwrap(), - 0, - source, - ); - - let extract_amount = self.smt.extract( - source_width.checked_sub(1).unwrap().try_into().unwrap(), - 0, - amount, - ); - (extract_source, extract_amount) - } else { - (source, amount) - }; - - // Do the rotate itself. - let rotate = self.encode_rotate(op, s, a, source_width); - - // Pad the extended result back to the full register bitwidth. Use the bits - // that were already in the source register. That is, given: - // reg - source width source width - // | | - // SOURCE: [ don't care bits | care bits ] - // - // dest width - // | - // OUT: [ same don't care bits | care bits ] - let unconstrained_bits = self.bitwidth.checked_sub(source_width).unwrap(); - - // If we are extending to the full register width, no padding needed - if unconstrained_bits == 0 || !self.find_widths { - rotate - } else { - let padding = self.smt.extract( - self.bitwidth.checked_sub(1).unwrap().try_into().unwrap(), - self.bitwidth - .checked_sub(unconstrained_bits) - .unwrap() - .try_into() - .unwrap(), - source, - ); - self.smt.concat(padding, rotate) - } - } - - // SMT-LIB only supports rotates by concrete amounts, but we - // need symbolic ones. This method essentially does if-conversion over possible - // concrete forms, outputting nested ITE blocks. We consider both the starting - // width and the rotate amount to be potentially symbolic. - // For safety, we add an assertion that some arm of this ITE must match. - fn rotate_symbolic_dyn_source_width( - &mut self, - source: SExpr, - source_width: SExpr, - amount: SExpr, - op: &str, - ) -> SExpr { - if self.find_widths { - return source; - } - let mut some_match = vec![]; - let mut ite_str = source; - - // Special case: if we are asked to rotate by 0, just return the source - let matching = self.smt.eq(self.bv(0, self.bitwidth), amount); - some_match.push(matching); - ite_str = self.smt.ite(matching, source, ite_str); - - // Possible starting widths - for possible_source in [8usize, 16, 32, 64] { - // Statement meaning the symbolic case matches this concrete case - let matching = self.smt.eq(self.smt.numeral(possible_source), source_width); - some_match.push(matching); - - // Extract the relevant bits of the source (which is modeled with a wider, - // register-width bitvector). - let extract_source = self.smt.extract( - possible_source.checked_sub(1).unwrap().try_into().unwrap(), - 0, - source, - ); - let extract_amount = self.smt.extract( - possible_source.checked_sub(1).unwrap().try_into().unwrap(), - 0, - amount, - ); - - // SMT bitvector rotate_left requires that the rotate amount be - // statically specified. Instead, to use a dynamic amount, desugar - // to shifts and bit arithmetic. - let rotate = self.encode_rotate(op, extract_source, extract_amount, possible_source); - - // Pad the extended result back to the full register bitwidth. Use the bits - // that were already in the source register. That is, given: - // reg - source width source width - // | | - // SOURCE: [ don't care bits | care bits ] - // - // dest width - // | - // OUT: [ same don't care bits | care bits ] - let unconstrained_bits = self.bitwidth.checked_sub(possible_source).unwrap(); - - // If we are extending to the full register width, no padding needed - let rotate = if unconstrained_bits == 0 { - rotate - } else { - let padding = self.smt.extract( - self.bitwidth.checked_sub(1).unwrap().try_into().unwrap(), - self.bitwidth - .checked_sub(unconstrained_bits) - .unwrap() - .try_into() - .unwrap(), - source, - ); - self.smt.concat(padding, rotate) - }; - - ite_str = self.smt.ite(matching, rotate, ite_str); - } - let some_shift_matches = self.smt.or_many(some_match); - self.width_assumptions.push(some_shift_matches); - ite_str - } - - pub fn widen_to_register_width( - &mut self, - tyvar: u32, - narrow_width: usize, - narrow_decl: SExpr, - name: Option, - ) -> SExpr { - let width = self.bitwidth.checked_sub(narrow_width).unwrap(); - if width > 0 { - let mut narrow_name = format!("narrow__{tyvar}"); - let mut wide_name = format!("wide__{tyvar}"); - if let Some(s) = name { - narrow_name = format!("{s}_{narrow_name}"); - wide_name = format!("{s}_{wide_name}"); - } - self.assume(self.smt.eq(self.smt.atom(&narrow_name), narrow_decl)); - self.additional_decls.push(( - narrow_name.clone(), - self.smt.bit_vec_sort(self.smt.numeral(narrow_width)), - )); - self.additional_decls.push(( - wide_name.clone(), - self.smt.bit_vec_sort(self.smt.numeral(self.bitwidth)), - )); - let padding = self.new_fresh_bits(width); - self.assume(self.smt.eq( - self.smt.atom(&wide_name), - self.smt.concat(padding, self.smt.atom(narrow_name)), - )); - self.smt.atom(wide_name) - } else if let Some(s) = name { - self.assume(self.smt.eq(self.smt.atom(&s), narrow_decl)); - self.smt.atom(&s) - } else { - narrow_decl - } - } - - pub fn get_expr_width_var(&self, e: &Expr) -> Option { - if let Some(tyvar) = self.tyctx.tyvars.get(e) { - self.width_vars.get(tyvar).map(|s| self.smt.atom(s)) - } else { - None - } - } - - pub fn vir_to_smt_ty(&self, ty: &Type) -> SExpr { - match ty { - Type::BitVector(w) => { - let width = w.unwrap_or(self.bitwidth); - self.smt.bit_vec_sort(self.smt.numeral(width)) - } - Type::Int => self.smt.int_sort(), - Type::Bool | Type::Unit => self.smt.bool_sort(), - } - } - - pub fn get_type(&self, x: &Expr) -> Option<&Type> { - self.tyctx.tymap.get(self.tyctx.tyvars.get(x)?) - } - - pub fn get_expr_value(&self, e: &Expr) -> Option { - if let Some(tyvar) = self.tyctx.tyvars.get(e) { - self.tyctx.tyvals.get(tyvar).copied() - } else { - None - } - } - - pub fn static_width(&self, x: &Expr) -> Option { - match self.get_type(x) { - Some(Type::BitVector(w)) => *w, - _ => None, - } - } - - pub fn assume_same_width(&mut self, x: &Expr, y: &Expr) { - let xw = self.get_expr_width_var(x).unwrap(); - let yw = self.get_expr_width_var(y).unwrap(); - self.width_assumptions.push(self.smt.eq(xw, yw)); - } - - pub fn assume_same_width_from_sexpr(&mut self, x: SExpr, y: &Expr) { - let yw = self.get_expr_width_var(y).unwrap(); - self.width_assumptions.push(self.smt.eq(x, yw)); - } - - pub fn assume_comparable_types(&mut self, x: &Expr, y: &Expr) { - match (self.get_type(x), self.get_type(y)) { - (None, _) | (_, None) => panic!("Missing type(s) {x:?} {y:?}"), - (Some(Type::Bool), Some(Type::Bool)) - | (Some(Type::Int), Some(Type::Int)) - | (Some(Type::Unit), Some(Type::Unit)) => (), - (Some(Type::BitVector(Some(xw))), Some(Type::BitVector(Some(yw)))) => { - assert_eq!(xw, yw, "incompatible {x:?} {y:?}") - } - (_, _) => self.assume_same_width(x, y), - } - } - - pub fn vir_expr_to_sexp(&mut self, e: Expr) -> SExpr { - let tyvar = self.tyctx.tyvars.get(&e); - let ty = self.get_type(&e); - let width = self.get_expr_width_var(&e); - let static_expr_width = self.static_width(&e); - match e { - Expr::Terminal(t) => match t { - Terminal::Literal(v, tyvar) => { - let lit = self.smt.atom(v); - if self.find_widths && matches!(ty.unwrap(), Type::BitVector(_)) { - self.widen_to_register_width(tyvar, static_expr_width.unwrap(), lit, None) - } else { - lit - } - } - Terminal::Var(v) => match self.var_map.get(&v) { - Some(o) => *o, - None => self.smt.atom(v), - }, - Terminal::Const(i, _) => match ty.unwrap() { - Type::BitVector(w) => { - let width = w.unwrap_or(self.bitwidth); - let narrow_decl = self.bv(i, width); - if self.find_widths { - self.zero_extend(self.bitwidth - width, narrow_decl) - } else { - narrow_decl - } - } - Type::Int => self.smt.numeral(i), - Type::Bool => { - if i == 0 { - self.smt.false_() - } else { - self.smt.true_() - } - } - Type::Unit => self.smt.true_(), - }, - Terminal::True => self.smt.true_(), - Terminal::False => self.smt.false_(), - Terminal::Wildcard(_) => match ty.unwrap() { - Type::BitVector(Some(w)) if !self.find_widths => self.new_fresh_bits(*w), - Type::BitVector(_) => self.new_fresh_bits(self.bitwidth), - Type::Int => self.new_fresh_int(), - Type::Bool => self.new_fresh_bool(), - Type::Unit => self.smt.true_(), - }, - }, - Expr::Unary(op, arg) => { - let op = match op { - UnaryOp::Not => "not", - UnaryOp::BVNeg => { - if self.find_widths { - self.assume_same_width_from_sexpr(width.unwrap(), &arg); - } - "bvneg" - } - UnaryOp::BVNot => { - if self.find_widths { - self.assume_same_width_from_sexpr(width.unwrap(), &arg); - } - "bvnot" - } - }; - let subexp = self.vir_expr_to_sexp(*arg); - self.smt.list(vec![self.smt.atom(op), subexp]) - } - Expr::Binary(op, x, y) => { - if self.find_widths { - match op { - BinaryOp::BVMul - | BinaryOp::BVUDiv - | BinaryOp::BVSDiv - | BinaryOp::BVUrem - | BinaryOp::BVSrem - | BinaryOp::BVAdd - | BinaryOp::BVSub - | BinaryOp::BVAnd - | BinaryOp::BVOr - | BinaryOp::BVShl - | BinaryOp::BVShr - | BinaryOp::BVAShr - | BinaryOp::BVRotl - | BinaryOp::BVRotr => self.assume_same_width_from_sexpr(width.unwrap(), &x), - BinaryOp::Eq => { - if let Some(Type::BitVector(_)) = self.get_type(&x) { - self.assume_comparable_types(&x, &y) - } - } - _ => (), - }; - self.assume_comparable_types(&x, &y); - } - match op { - BinaryOp::BVRotl => { - let source_width = self.static_width(&x); - match source_width { - Some(w) => { - let xs = self.vir_expr_to_sexp(*x); - let ys = self.vir_expr_to_sexp(*y); - return self.rotate_symbolic(xs, w, ys, "rotate_left"); - } - None => { - let arg_width = self.get_expr_width_var(&x).unwrap(); - let xs = self.vir_expr_to_sexp(*x); - let ys = self.vir_expr_to_sexp(*y); - return self.rotate_symbolic_dyn_source_width( - xs, - arg_width, - ys, - "rotate_left", - ); - } - } - } - BinaryOp::BVRotr => { - let source_width = self.static_width(&x); - match source_width { - Some(w) => { - let xs = self.vir_expr_to_sexp(*x); - let ys = self.vir_expr_to_sexp(*y); - return self.rotate_symbolic(xs, w, ys, "rotate_right"); - } - None => { - let arg_width = self.get_expr_width_var(&x).unwrap(); - let xs = self.vir_expr_to_sexp(*x); - let ys = self.vir_expr_to_sexp(*y); - return self.rotate_symbolic_dyn_source_width( - xs, - arg_width, - ys, - "rotate_right", - ); - } - } - } - // To shift right, we need to make sure the bits to the right get zeroed. Shift left first. - BinaryOp::BVShr => { - let arg_width = if self.find_widths { - self.get_expr_width_var(&x).unwrap() - } else { - self.smt.numeral(self.static_width(&x).unwrap()) - }; - let xs = self.vir_expr_to_sexp(*x); - - // Strategy: shift left by (bitwidth - arg width) to zero bits to the right - // of the bits in the argument size. Then shift right by (amt + (bitwidth - arg width)) - - // Width math - if self.find_widths { - // The shift arg needs to be extracted to the right width, default to 8 if unknown - let y_static_width = self.static_width(&y).unwrap_or(8); - let y_rec = self.vir_expr_to_sexp(*y); - if self.find_widths { - return xs; - } - let extract = self.smt.extract( - y_static_width.checked_sub(1).unwrap().try_into().unwrap(), - 0, - y_rec, - ); - let ys = self.zero_extend(self.bitwidth - y_static_width, extract); - let arg_width_as_bv = self.int2bv(self.bitwidth, arg_width); - let bitwidth_as_bv = - self.bv(self.bitwidth.try_into().unwrap(), self.bitwidth); - let extra_shift = self.smt.bvsub(bitwidth_as_bv, arg_width_as_bv); - let shl_to_zero = self.smt.bvshl(xs, extra_shift); - - let amt_plus_extra = self.smt.bvadd(ys, extra_shift); - return self.smt.bvlshr(shl_to_zero, amt_plus_extra); - } else { - let ys = self.vir_expr_to_sexp(*y); - return self.smt.bvlshr(xs, ys); - } - } - BinaryOp::BVAShr => { - let arg_width = if self.find_widths { - self.get_expr_width_var(&x).unwrap() - } else { - self.smt.numeral(self.static_width(&x).unwrap()) - }; - let xs = self.vir_expr_to_sexp(*x); - - // Strategy: shift left by (bitwidth - arg width) to eliminate bits to the left - // of the bits in the argument size. Then shift right by (amt + (bitwidth - arg width)) - - // Width math - if self.find_widths { - // The shift arg needs to be extracted to the right width, default to 8 if unknown - let y_static_width = self.static_width(&y).unwrap_or(8); - let ys = self.vir_expr_to_sexp(*y); - let extract = self.smt.extract( - y_static_width.checked_sub(1).unwrap().try_into().unwrap(), - 0, - ys, - ); - let ysext = self.zero_extend(self.bitwidth - y_static_width, extract); - - let arg_width_as_bv = self.int2bv(self.bitwidth, arg_width); - let bitwidth_as_bv = - self.bv(self.bitwidth.try_into().unwrap(), self.bitwidth); - let extra_shift = self.smt.bvsub(bitwidth_as_bv, arg_width_as_bv); - let shl_to_zero = self.smt.bvshl(xs, extra_shift); - - let amt_plus_extra = self.smt.bvadd(ysext, extra_shift); - return self.smt.bvashr(shl_to_zero, amt_plus_extra); - } else { - let ys = self.vir_expr_to_sexp(*y); - return self.smt.bvashr(xs, ys); - } - } - _ => (), - }; - let op_str = match op { - BinaryOp::And => "and", - BinaryOp::Or => "or", - BinaryOp::Imp => "=>", - BinaryOp::Eq => "=", - BinaryOp::Lte => match (self.get_type(&x), self.get_type(&y)) { - (Some(Type::Int), Some(Type::Int)) => "<=", - (Some(Type::BitVector(_)), Some(Type::BitVector(_))) => "bvule", - _ => unreachable!(), - }, - BinaryOp::Lt => match (self.get_type(&x), self.get_type(&y)) { - (Some(Type::Int), Some(Type::Int)) => "<", - (Some(Type::BitVector(_)), Some(Type::BitVector(_))) => "bvult", - _ => unreachable!(), - }, - BinaryOp::BVSgt => "bvsgt", - BinaryOp::BVSgte => "bvsge", - BinaryOp::BVSlt => "bvslt", - BinaryOp::BVSlte => "bvsle", - BinaryOp::BVUgt => "bvugt", - BinaryOp::BVUgte => "bvuge", - BinaryOp::BVUlt => "bvult", - BinaryOp::BVUlte => "bvule", - BinaryOp::BVMul => "bvmul", - BinaryOp::BVUDiv => "bvudiv", - BinaryOp::BVSDiv => "bvsdiv", - BinaryOp::BVAdd => "bvadd", - BinaryOp::BVSub => "bvsub", - BinaryOp::BVUrem => "bvurem", - BinaryOp::BVSrem => "bvsrem", - BinaryOp::BVAnd => "bvand", - BinaryOp::BVOr => "bvor", - BinaryOp::BVXor => "bvxor", - BinaryOp::BVShl => "bvshl", - BinaryOp::BVSaddo => "bvsaddo", - _ => unreachable!("{:?}", op), - }; - // If we have some static width that isn't the bitwidth, extract based on it - // before performing the operation for the dynamic case. - match static_expr_width { - Some(w) if w < self.bitwidth && self.find_widths => { - let h: i32 = (w - 1).try_into().unwrap(); - let x_sexp = self.vir_expr_to_sexp(*x); - let y_sexp = self.vir_expr_to_sexp(*y); - self.zero_extend( - self.bitwidth.checked_sub(w).unwrap(), - self.smt.list(vec![ - self.smt.atom(op_str), - self.smt.extract(h, 0, x_sexp), - self.smt.extract(h, 0, y_sexp), - ]), - ) - } - _ => { - let x_sexp = self.vir_expr_to_sexp(*x); - let y_sexp = self.vir_expr_to_sexp(*y); - self.smt.list(vec![self.smt.atom(op_str), x_sexp, y_sexp]) - } - } - } - Expr::BVIntToBV(w, x) => { - let x_sexp = self.vir_expr_to_sexp(*x); - if self.find_widths { - let padded_width = self.bitwidth - w; - self.zero_extend(padded_width, self.int2bv(w, x_sexp)) - } else { - self.int2bv(w, x_sexp) - } - } - Expr::BVToInt(x) => { - let x_sexp = self.vir_expr_to_sexp(*x); - self.bv2nat(x_sexp) - } - Expr::BVZeroExtTo(i, x) => { - let arg_width = if self.find_widths { - let expr_width = width.unwrap(); - self.width_assumptions - .push(self.smt.eq(expr_width, self.smt.numeral(i))); - self.get_expr_width_var(&x).unwrap() - } else { - self.smt.numeral(self.static_width(&x).unwrap()) - }; - let static_width = self.static_width(&x); - let xs = self.vir_expr_to_sexp(*x); - if let Some(size) = static_width { - self.extend_concrete(i, xs, size, "zero_extend") - } else { - self.extend_symbolic(self.smt.numeral(i), xs, arg_width, "zero_extend") - } - } - Expr::BVZeroExtToVarWidth(i, x) => { - let static_arg_width = self.static_width(&x); - let arg_width = self.get_expr_width_var(&x); - let is = self.vir_expr_to_sexp(*i); - let xs = self.vir_expr_to_sexp(*x); - if self.find_widths { - let expr_width = width.unwrap(); - self.width_assumptions.push(self.smt.eq(expr_width, is)); - } - if let (Some(arg_size), Some(e_size)) = (static_arg_width, static_expr_width) { - self.extend_concrete(e_size, xs, arg_size, "zero_extend") - } else { - self.extend_symbolic(is, xs, arg_width.unwrap(), "zero_extend") - } - } - Expr::BVSignExtTo(i, x) => { - let arg_width = if self.find_widths { - let expr_width = width.unwrap(); - self.width_assumptions - .push(self.smt.eq(expr_width, self.smt.numeral(i))); - self.get_expr_width_var(&x).unwrap() - } else { - self.smt.numeral(self.static_width(&x).unwrap()) - }; - let static_width = self.static_width(&x); - let xs = self.vir_expr_to_sexp(*x); - if let Some(size) = static_width { - self.extend_concrete(i, xs, size, "sign_extend") - } else { - self.extend_symbolic(self.smt.numeral(i), xs, arg_width, "sign_extend") - } - } - Expr::BVSignExtToVarWidth(i, x) => { - let static_arg_width = self.static_width(&x); - let arg_width = self.get_expr_width_var(&x); - let is = self.vir_expr_to_sexp(*i); - let xs = self.vir_expr_to_sexp(*x); - if self.find_widths { - let expr_width = width.unwrap(); - self.width_assumptions.push(self.smt.eq(expr_width, is)); - } - if let (Some(arg_size), Some(e_size)) = (static_arg_width, static_expr_width) { - self.extend_concrete(e_size, xs, arg_size, "sign_extend") - } else { - self.extend_symbolic(is, xs, arg_width.unwrap(), "sign_extend") - } - } - Expr::BVConvTo(x, y) => { - if self.find_widths { - let expr_width = width.unwrap(); - let dyn_width = self.vir_expr_to_sexp(*x); - let eq = self.smt.eq(expr_width, dyn_width); - self.width_assumptions.push(eq); - self.vir_expr_to_sexp(*y) - } else { - let arg_width = self.static_width(&y).unwrap(); - match ty { - Some(Type::BitVector(Some(w))) => match arg_width.cmp(w) { - Ordering::Less => { - let padding = - self.new_fresh_bits(w.checked_sub(arg_width).unwrap()); - let ys = self.vir_expr_to_sexp(*y); - self.smt.concat(padding, ys) - } - Ordering::Greater => { - let new = (w - 1).try_into().unwrap(); - let ys = self.vir_expr_to_sexp(*y); - self.smt.extract(new, 0, ys) - } - Ordering::Equal => self.vir_expr_to_sexp(*y), - }, - _ => unreachable!("{:?}, {:?}", x, y), - } - } - } - Expr::WidthOf(x) => { - if self.find_widths { - self.get_expr_width_var(&x).unwrap() - } else { - self.smt.numeral(self.static_width(&x).unwrap()) - } - } - Expr::BVExtract(i, j, x) => { - assert!(i >= j); - if self.get_type(&x).is_some() { - let xs = self.vir_expr_to_sexp(*x); - // No-op if we are extracting exactly the full bitwidth - if j == 0 && i == self.bitwidth - 1 && self.find_widths { - return xs; - } - let extract = - self.smt - .extract(i.try_into().unwrap(), j.try_into().unwrap(), xs); - let new_width = i - j + 1; - if new_width < self.bitwidth && self.find_widths { - let padding = - self.new_fresh_bits(self.bitwidth.checked_sub(new_width).unwrap()); - self.smt.concat(padding, extract) - } else { - extract - } - } else { - unreachable!("Must perform extraction on bv with known width") - } - } - Expr::Conditional(c, t, e) => { - if self.find_widths && matches!(ty, Some(Type::BitVector(_))) { - self.assume_same_width_from_sexpr(width.unwrap(), &t); - self.assume_same_width_from_sexpr(width.unwrap(), &e); - } - let cs = self.vir_expr_to_sexp(*c); - let ts = self.vir_expr_to_sexp(*t); - let es = self.vir_expr_to_sexp(*e); - self.smt.ite(cs, ts, es) - } - Expr::Switch(c, cases) => { - if self.find_widths { - if matches!(ty, Some(Type::BitVector(_))) { - for (_, b) in &cases { - self.assume_same_width_from_sexpr(width.unwrap(), b); - } - } - let cty = self.get_type(&c); - if matches!(cty, Some(Type::BitVector(_))) { - let cwidth = self.get_expr_width_var(&c); - for (m, _) in &cases { - self.assume_same_width_from_sexpr(cwidth.unwrap(), m); - } - } - } - let cs = self.vir_expr_to_sexp(*c); - let mut case_sexprs: Vec<(SExpr, SExpr)> = cases - .iter() - .map(|(m, b)| { - ( - self.vir_expr_to_sexp(m.clone()), - self.vir_expr_to_sexp(b.clone()), - ) - }) - .collect(); - - // Assert that some case must match - let some_case_matches: Vec = case_sexprs - .iter() - .map(|(m, _)| self.smt.eq(cs, *m)) - .collect(); - self.assert(self.smt.or_many(some_case_matches.clone())); - - let (_, last_body) = case_sexprs.remove(case_sexprs.len() - 1); - - // Reverse to keep the order of the switch - case_sexprs.iter().rev().fold(last_body, |acc, (m, b)| { - self.smt.ite(self.smt.eq(cs, *m), *b, acc) - }) - } - Expr::CLZ(e) => { - let tyvar = *tyvar.unwrap(); - if self.find_widths { - self.assume_same_width_from_sexpr(width.unwrap(), &e); - } - let es = self.vir_expr_to_sexp(*e); - match static_expr_width { - Some(1) => clz::clz1(self, es, tyvar), - Some(8) => clz::clz8(self, es, tyvar), - Some(16) => clz::clz16(self, es, tyvar), - Some(32) => clz::clz32(self, es, tyvar), - Some(64) => clz::clz64(self, es, tyvar), - Some(w) => unreachable!("Unexpected CLZ width {}", w), - None => unreachable!("Need static CLZ width"), - } - } - Expr::CLS(e) => { - let tyvar = *tyvar.unwrap(); - if self.find_widths { - self.assume_same_width_from_sexpr(width.unwrap(), &e); - } - let es = self.vir_expr_to_sexp(*e); - match static_expr_width { - Some(1) => cls::cls1(self, tyvar), - Some(8) => cls::cls8(self, es, tyvar), - Some(16) => cls::cls16(self, es, tyvar), - Some(32) => cls::cls32(self, es, tyvar), - Some(64) => cls::cls64(self, es, tyvar), - Some(w) => unreachable!("Unexpected CLS width {}", w), - None => unreachable!("Need static CLS width"), - } - } - Expr::Rev(e) => { - let tyvar = *tyvar.unwrap(); - if self.find_widths { - self.assume_same_width_from_sexpr(width.unwrap(), &e); - } - let es = self.vir_expr_to_sexp(*e); - match static_expr_width { - Some(1) => rev::rev1(self, es, tyvar), - Some(8) => rev::rev8(self, es, tyvar), - Some(16) => rev::rev16(self, es, tyvar), - Some(32) => rev::rev32(self, es, tyvar), - Some(64) => rev::rev64(self, es, tyvar), - Some(w) => unreachable!("Unexpected CLS width {}", w), - None => unreachable!("Need static CLS width"), - } - } - Expr::BVSubs(ty, x, y) => { - let tyvar = *tyvar.unwrap(); - if self.find_widths { - self.assume_comparable_types(&x, &y); - } - let ety = self.vir_expr_to_sexp(*ty); - let ex = self.vir_expr_to_sexp(*x); - let ey = self.vir_expr_to_sexp(*y); - - let encoded_32 = subs::subs(self, 32, ex, ey, tyvar); - let encoded_64 = subs::subs(self, 64, ex, ey, tyvar); - - self.smt.ite( - self.smt.eq(ety, self.smt.numeral(32)), - encoded_32, - encoded_64, - ) - } - Expr::BVPopcnt(x) => { - let tyvar = *tyvar.unwrap(); - if self.find_widths { - self.assume_same_width_from_sexpr(width.unwrap(), &x); - } - let ex = self.vir_expr_to_sexp(*x); - - match static_expr_width { - Some(8) => { - let p = popcnt(self, 8, ex, tyvar); - if self.find_widths { - self.zero_extend(self.bitwidth - 8, p) - } else { - p - } - } - Some(16) => { - let p = popcnt(self, 16, ex, tyvar); - if self.find_widths { - self.zero_extend(self.bitwidth - 8, p) - } else { - self.zero_extend(8, p) - } - } - Some(32) => { - let p = popcnt(self, 32, ex, tyvar); - if self.find_widths { - self.zero_extend(self.bitwidth - 8, p) - } else { - self.zero_extend(24, p) - } - } - Some(64) => { - let p = popcnt(self, 64, ex, tyvar); - if self.find_widths { - self.zero_extend(self.bitwidth - 8, p) - } else { - self.zero_extend(56, p) - } - } - Some(w) => unreachable!("Unexpected popcnt width {}", w), - None => unreachable!("Need static popcnt width"), - } - } - Expr::BVConcat(xs) => { - if self.find_widths { - let widths: Vec = xs - .iter() - .map(|x| self.get_expr_width_var(x).unwrap()) - .collect(); - let sum = self.smt.plus_many(widths); - self.width_assumptions - .push(self.smt.eq(width.unwrap(), sum)); - } - let mut sexprs: Vec = xs - .iter() - .map(|x| self.vir_expr_to_sexp(x.clone())) - .collect(); - let last = sexprs.remove(sexprs.len() - 1); - - // Width hack for now - if self.find_widths { - return sexprs[0]; - } - // Reverse to keep the order of the cases - sexprs - .iter() - .rev() - .fold(last, |acc, x| self.smt.concat(*x, acc)) - } - Expr::LoadEffect(x, y, z) => { - let ex = self.vir_expr_to_sexp(*x); - let ey = self.vir_expr_to_sexp(*y); - let ez = self.vir_expr_to_sexp(*z); - - if self.find_widths { - self.width_assumptions.push(self.smt.eq(width.unwrap(), ey)); - } - - if self.lhs_flag { - if self.lhs_load_args.is_some() { - panic!("Only one load on the LHS currently supported, found multiple.") - } - self.lhs_load_args = Some(vec![ex, ey, ez]); - let load_ret = if self.find_widths { - self.new_fresh_bits(self.bitwidth) - } else { - self.new_fresh_bits(static_expr_width.unwrap()) - }; - self.load_return = Some(load_ret); - load_ret - } else { - if self.rhs_load_args.is_some() { - panic!("Only one load on the RHS currently supported, found multiple.") - } - self.rhs_load_args = Some(vec![ex, ey, ez]); - self.load_return.unwrap() - } - } - Expr::StoreEffect(w, x, y, z) => { - let ew = self.vir_expr_to_sexp(*w); - let ex = self.vir_expr_to_sexp(*x); - let ez = self.vir_expr_to_sexp(*z); - - if self.find_widths { - let y_width = self.get_expr_width_var(&y).unwrap(); - self.width_assumptions.push(self.smt.eq(y_width, ex)); - } - let ey = self.vir_expr_to_sexp(*y); - - if self.lhs_flag { - self.lhs_store_args = Some(vec![ew, ex, ey, ez]); - } else { - self.rhs_store_args = Some(vec![ew, ex, ey, ez]); - } - self.smt.atom("true") - } - } - } - - // Checks whether the assumption list is always false - fn check_assumptions_feasibility( - &mut self, - assumptions: &[SExpr], - term_input_bs: &[String], - config: &Config, - ) -> VerificationResult { - log::debug!("Checking assumption feasibility"); - self.smt.push().unwrap(); - for (i, a) in assumptions.iter().enumerate() { - self.smt - .assert(self.smt.named(format!("assume{i}"), *a)) - .unwrap(); - } - - let res = match self.smt.check() { - Ok(Response::Sat) => { - if !config.distinct_check || term_input_bs.is_empty() { - log::debug!("Assertion list is feasible for at least one input!"); - self.smt.pop().unwrap(); - return VerificationResult::Success; - } - // Check that there is a model with distinct bitvector inputs - let mut not_all_same = vec![]; - let atoms: Vec = term_input_bs.iter().map(|n| self.smt.atom(n)).collect(); - let solution = self.smt.get_value(atoms).unwrap(); - assert_eq!(term_input_bs.len(), solution.len()); - for (variable, value) in solution { - not_all_same.push(self.smt.not(self.smt.eq(variable, value))); - } - match not_all_same.len().cmp(&1) { - Ordering::Equal => self.smt.assert(not_all_same[0]).unwrap(), - Ordering::Greater => self.smt.assert(self.smt.and_many(not_all_same)).unwrap(), - Ordering::Less => unreachable!("must have some BV inputs"), - } - match self.smt.check() { - Ok(Response::Sat) => { - log::debug!("Assertion list is feasible for two distinct inputs"); - VerificationResult::Success - } - Ok(Response::Unsat) => { - log::debug!( - "Assertion list is only feasible for one input with distinct BV values!" - ); - VerificationResult::NoDistinctModels - } - Ok(Response::Unknown) => { - panic!("Solver said 'unk'"); - } - Err(err) => { - unreachable!("Error! {:?}", err); - } - } - } - Ok(Response::Unsat) => { - log::debug!("Assertion list is infeasible!"); - let unsat = self.smt.get_unsat_core().unwrap(); - log::debug!("Unsat core:\n{}", self.smt.display(unsat)); - VerificationResult::InapplicableRule - } - Ok(Response::Unknown) => { - panic!("Solver said 'unk'"); - } - Err(err) => { - unreachable!("Error! {:?}", err); - } - }; - self.smt.pop().unwrap(); - res - } - - fn display_hex_to_bin(&self, value: SExpr) -> String { - let sexpr_hex_prefix = "#x"; - let val_str = self.smt.display(value).to_string(); - if val_str.starts_with(sexpr_hex_prefix) { - let without_prefix = val_str.trim_start_matches("#x"); - let as_unsigned = u128::from_str_radix(without_prefix, 16).unwrap(); - // SMT-LIB: bvhexX where X is a hexadecimal numeral of length m defines the bitvector - // constant with value X and size 4*m. - match without_prefix.len() { - 2 => format!("{}|{:#010b}", self.smt.display(value), as_unsigned), - 3 => format!("{}|{:#014b}", self.smt.display(value), as_unsigned), - 4 => format!("{}|{:#018b}", self.smt.display(value), as_unsigned), - 8 => format!("{}|{:#034b}", self.smt.display(value), as_unsigned), - 16 => format!("{}|{:#068b}", self.smt.display(value), as_unsigned), - 17 => format!("{}|{:#070b}", self.smt.display(value), as_unsigned), - 32 => format!("{}|{:#0130b}", self.smt.display(value), as_unsigned), - _ => { - format!("{}|{:#b}", self.smt.display(value), as_unsigned) - } - } - } else { - val_str - } - } - - fn display_value(&self, variable: SExpr, value: SExpr) -> (String, String) { - let var_str = self.smt.display(variable).to_string(); - (var_str, self.display_hex_to_bin(value)) - } - - fn display_isle_pattern( - &mut self, - termenv: &TermEnv, - typeenv: &TypeEnv, - vars: &Vec<(String, String)>, - rule: &Rule, - pat: &Pattern, - ) -> SExpr { - let mut to_sexpr = |p| self.display_isle_pattern(termenv, typeenv, vars, rule, p); - - match pat { - isle::sema::Pattern::Term(_, term_id, args) => { - let sym = termenv.terms[term_id.index()].name; - let name = typeenv.syms[sym.index()].clone(); - - let mut sexprs = args.iter().map(&mut to_sexpr).collect::>(); - - sexprs.insert(0, self.smt.atom(name)); - self.smt.list(sexprs) - } - isle::sema::Pattern::Var(_, var_id) => { - let sym = rule.vars[var_id.index()].name; - let ident = typeenv.syms[sym.index()].clone(); - let smt_ident_prefix = format!("{}__clif{}__", ident, var_id.index()); - - let var = self.display_var_from_smt_prefix(vars, &ident, &smt_ident_prefix); - self.smt.atom(var) - } - isle::sema::Pattern::BindPattern(_, var_id, subpat) => { - let sym = rule.vars[var_id.index()].name; - let ident = &typeenv.syms[sym.index()]; - let smt_ident_prefix = format!("{}__clif{}__", ident, var_id.index(),); - let subpat_node = to_sexpr(subpat); - - let var = self.display_var_from_smt_prefix(vars, ident, &smt_ident_prefix); - - // Special case: elide bind patterns to wildcars - if matches!(**subpat, isle::sema::Pattern::Wildcard(_)) { - self.smt.atom(var) - } else { - self.smt - .list(vec![self.smt.atom(var), self.smt.atom("@"), subpat_node]) - } - } - isle::sema::Pattern::Wildcard(_) => self.smt.list(vec![self.smt.atom("_")]), - - isle::sema::Pattern::ConstPrim(_, sym) => { - let name = typeenv.syms[sym.index()].clone(); - self.smt.list(vec![self.smt.atom(name)]) - } - isle::sema::Pattern::ConstBool(_, val) => { - self.smt.list(vec![self.smt.atom(format!("{val}"))]) - } - isle::sema::Pattern::ConstInt(_, num) => { - let _smt_name_prefix = format!("{num}__"); - self.smt.list(vec![self.smt.atom(num.to_string())]) - } - isle::sema::Pattern::And(_, subpats) => { - let mut sexprs = subpats.iter().map(to_sexpr).collect::>(); - - sexprs.insert(0, self.smt.atom("and")); - self.smt.list(sexprs) - } - } - } - - fn display_var_from_smt_prefix( - &self, - vars: &Vec<(String, String)>, - ident: &str, - prefix: &str, - ) -> String { - let matches: Vec<&(String, String)> = - vars.iter().filter(|(v, _)| v.starts_with(prefix)).collect(); - if matches.is_empty() { - panic!("Can't find match for: {prefix}\n{vars:?}"); - } else if matches.len() == 3 { - assert!( - self.find_widths, - "Only expect multiple matches with dynamic widths" - ); - for (name, model) in matches { - if name.contains("narrow") { - return format!("[{}|{}]", self.smt.display(self.smt.atom(ident)), model); - } - } - panic!("narrow not found"); - } else if matches.len() == 1 { - let model = &matches.first().unwrap().1; - format!("[{}|{}]", self.smt.display(self.smt.atom(ident)), model) - } else { - panic!("Unexpected number of matches!") - } - } - - fn display_isle_expr( - &self, - termenv: &TermEnv, - typeenv: &TypeEnv, - vars: &Vec<(String, String)>, - rule: &Rule, - expr: &isle::sema::Expr, - ) -> SExpr { - let to_sexpr = |e| self.display_isle_expr(termenv, typeenv, vars, rule, e); - - match expr { - isle::sema::Expr::Term(_, term_id, args) => { - let sym = termenv.terms[term_id.index()].name; - let name = typeenv.syms[sym.index()].clone(); - - let mut sexprs = args.iter().map(to_sexpr).collect::>(); - - sexprs.insert(0, self.smt.atom(name)); - self.smt.list(sexprs) - } - isle::sema::Expr::Var(_, var_id) => { - let sym = rule.vars[var_id.index()].name; - let ident = typeenv.syms[sym.index()].clone(); - let smt_ident_prefix = format!("{}__clif{}__", ident, var_id.index()); - - let var = self.display_var_from_smt_prefix(vars, &ident, &smt_ident_prefix); - self.smt.atom(var) - } - isle::sema::Expr::ConstPrim(_, sym) => { - let name = typeenv.syms[sym.index()].clone(); - self.smt.list(vec![self.smt.atom(name)]) - } - isle::sema::Expr::ConstBool(_, val) => { - self.smt.list(vec![self.smt.atom(format!("{val}"))]) - } - isle::sema::Expr::ConstInt(_, num) => { - let _smt_name_prefix = format!("{num}__"); - self.smt.list(vec![self.smt.atom(num.to_string())]) - } - isle::sema::Expr::Let { bindings, body, .. } => { - let mut sexprs = vec![]; - for (varid, _, expr) in bindings { - let sym = rule.vars[varid.index()].name; - let ident = typeenv.syms[sym.index()].clone(); - let smt_prefix = format!("{}__clif{}__", ident, varid.index()); - let var = self.display_var_from_smt_prefix(vars, &ident, &smt_prefix); - - sexprs.push(self.smt.list(vec![self.smt.atom(var), to_sexpr(expr)])); - } - self.smt.list(vec![ - self.smt.atom("let"), - self.smt.list(sexprs), - to_sexpr(body), - ]) - } - } - } - - fn display_model( - &mut self, - termenv: &TermEnv, - typeenv: &TypeEnv, - rule: &Rule, - lhs_sexpr: SExpr, - rhs_sexpr: SExpr, - ) { - let mut vars = vec![]; - let mut lhs_value = None; - let mut rhs_value = None; - for (name, atom) in &self.var_map { - let solution = self - .smt - .get_value(vec![self.smt.atom(name), *atom]) - .unwrap(); - for (variable, value) in solution { - let display = self.display_value(variable, value); - vars.push(display.clone()); - if variable == lhs_sexpr { - lhs_value = Some(display.1); - } else if variable == rhs_sexpr { - rhs_value = Some(display.1); - } - } - } - for (name, _) in &self.additional_decls { - let solution = self.smt.get_value(vec![self.smt.atom(name)]).unwrap(); - for (variable, value) in solution { - vars.push(self.display_value(variable, value)); - } - } - vars.sort_by_key(|x| x.0.clone()); - vars.dedup(); - - // TODO VERBOSE - println!("Counterexample summary"); - let lhs = self.display_isle_pattern( - termenv, - typeenv, - &vars, - rule, - &Pattern::Term( - cranelift_isle::sema::TypeId(0), - rule.root_term, - rule.args.clone(), - ), - ); - println!("{}", self.smt.display(lhs)); - - // if-let statement processing - if !&rule.iflets.is_empty() { - print!("(if-let "); - } - for if_let_struct in &rule.iflets { - let if_lhs = &if_let_struct.lhs; - let if_rhs: &cranelift_isle::sema::Expr = &if_let_struct.rhs; - - let if_lhs_expr = self.display_isle_pattern(termenv, typeenv, &vars, rule, if_lhs); - - let if_rhs_expr = self.display_isle_expr(termenv, typeenv, &vars, rule, if_rhs); - - println!( - "({} {})", - self.smt.display(if_lhs_expr), - self.smt.display(if_rhs_expr) - ); - } - println!(")"); - - println!("=>"); - let rhs = self.display_isle_expr(termenv, typeenv, &vars, rule, &rule.rhs); - println!("{}", self.smt.display(rhs)); - - println!("\n{} =>\n{}\n", lhs_value.unwrap(), rhs_value.unwrap(),); - } - - fn declare_variables( - &mut self, - rule_sem: &RuleSemantics, - config: &Config, - ) -> (Vec, Vec) { - let mut assumptions: Vec = vec![]; - log::trace!("Declaring quantified variables"); - for v in &rule_sem.quantified_vars { - let name = &v.name; - let ty = self.tyctx.tymap[&v.tyvar]; - let var_ty = self.vir_to_smt_ty(&ty); - log::trace!("\t{} : {}", name, self.smt.display(var_ty)); - if let Type::BitVector(w) = ty { - if self.find_widths { - let wide = self.widen_to_register_width( - v.tyvar, - w.unwrap_or(self.bitwidth), - self.smt.atom(name), - Some(name.to_string()), - ); - self.var_map.insert(name.clone(), wide); - } else { - self.var_map.insert(name.clone(), self.smt.atom(name)); - } - } else { - self.var_map.insert(name.clone(), self.smt.atom(name)); - } - self.smt.declare_const(name, var_ty).unwrap(); - } - self.lhs_flag = true; - for a in &rule_sem.lhs_assumptions { - let p = self.vir_expr_to_sexp(a.clone()); - assumptions.push(p) - } - self.lhs_flag = false; - for a in &rule_sem.rhs_assumptions { - let p = self.vir_expr_to_sexp(a.clone()); - assumptions.push(p) - } - if self.find_widths { - for a in &self.width_assumptions { - assumptions.push(*a); - } - } - self.additional_assumptions.is_empty(); - for a in &self.additional_assumptions { - assumptions.push(*a); - } - // Look at RHS assertions, which are checked, not trusted - let assertions: Vec = rule_sem - .rhs_assertions - .iter() - .map(|a| self.vir_expr_to_sexp(a.clone())) - .collect(); - - for (name, ty) in &self.additional_decls { - self.smt.declare_const(name, *ty).unwrap(); - } - - if let Some(a) = &config.custom_assumptions { - let term_args = rule_sem - .term_args - .iter() - .map(|s| self.smt.atom(s)) - .collect(); - let custom_assumptions = a(&self.smt, term_args); - log::debug!( - "Custom assumptions:\n\t{}\n", - self.smt.display(custom_assumptions) - ); - assumptions.push(custom_assumptions); - } - (assumptions, assertions) - } -} - -/// Overall query for single rule: -/// -/// ```text -/// -/// (not (=> (= )))))) -/// ``` -pub fn run_solver( - rule_sem: &RuleSemantics, - rule: &Rule, - termenv: &TermEnv, - typeenv: &TypeEnv, - concrete: &Option, - config: &Config, - _types: &TermSignature, -) -> VerificationResult { - if std::env::var("SKIP_SOLVER").is_ok() { - log::debug!("Environment variable SKIP_SOLVER set, returning Unknown"); - return VerificationResult::Unknown; - } - - let mut solver = easy_smt::ContextBuilder::new() - .replay_file(Some(std::fs::File::create("dynamic_widths.smt2").unwrap())) - .solver("z3", ["-smt2", "-in"]) - .build() - .unwrap(); - - solver - .set_option(":produce-unsat-cores", solver.true_()) - .unwrap(); - - // We start with logic to determine the width of all bitvectors - let mut ctx = SolverCtx { - smt: solver, - // Always find widths at first - find_widths: true, - tyctx: rule_sem.tyctx.clone(), - bitwidth: MAX_WIDTH, - var_map: HashMap::new(), - width_vars: HashMap::new(), - width_assumptions: vec![], - additional_decls: vec![], - additional_assumptions: vec![], - additional_assertions: vec![], - fresh_bits_idx: 0, - lhs_load_args: None, - rhs_load_args: None, - lhs_store_args: None, - rhs_store_args: None, - load_return: None, - lhs_flag: true, - }; - - let mut unresolved_widths = vec![]; - - // Check whether the non-solver type inference was able to resolve all bitvector widths, - // and add assumptions for known widths - for (_e, t) in &ctx.tyctx.tyvars { - let ty = &ctx.tyctx.tymap[t]; - if let Type::BitVector(w) = ty { - let width_name = format!("width__{t}"); - ctx.additional_decls - .push((width_name.clone(), ctx.smt.int_sort())); - match *w { - Some(bitwidth) => { - let eq = ctx - .smt - .eq(ctx.smt.atom(&width_name), ctx.smt.numeral(bitwidth)); - ctx.width_assumptions.push(eq); - } - None => { - log::debug!("Unresolved width: {:?} ({})", &_e, *t); - ctx.width_assumptions - .push(ctx.smt.gt(ctx.smt.atom(&width_name), ctx.smt.numeral(0))); - unresolved_widths.push(width_name.clone()); - } - }; - ctx.width_vars.insert(*t, width_name.clone()); - } - } - - if unresolved_widths.is_empty() { - log::debug!("All widths resolved after basic type inference"); - return run_solver_with_static_widths( - &RuleCtx { - rule_sem, - rule, - termenv, - typeenv, - config, - }, - &ctx.tyctx, - concrete, - ); - } - - log::debug!("Some unresolved widths after basic type inference"); - log::debug!("Finding widths from the solver"); - ctx.find_widths = true; - let (assumptions, _) = ctx.declare_variables(rule_sem, config); - ctx.smt.push().unwrap(); - for (i, a) in assumptions.iter().enumerate() { - ctx.smt - .assert(ctx.smt.named(format!("dyn{i}"), *a)) - .unwrap(); - } - - resolve_dynamic_widths( - RuleCtx { - rule_sem, - rule, - termenv, - typeenv, - config, - }, - concrete, - &mut ctx, - unresolved_widths, - 0, - ) -} - -fn resolve_dynamic_widths( - rulectx: RuleCtx, - concrete: &Option, - ctx: &mut SolverCtx, - unresolved_widths: Vec, - attempt: usize, -) -> VerificationResult { - if attempt > 10 { - panic!("Unexpected number of attempts to resolve dynamic widths!") - } - match ctx.smt.check() { - Ok(Response::Sat) => { - let mut cur_tyctx = ctx.tyctx.clone(); - let mut width_resolutions = HashMap::new(); - for (e, t) in &ctx.tyctx.tyvars { - let ty = &ctx.tyctx.tymap[t]; - if let Type::BitVector(w) = ty { - let width_name = format!("width__{t}"); - let atom = ctx.smt.atom(&width_name); - let width = ctx.smt.get_value(vec![atom]).unwrap().first().unwrap().1; - let width_int = u8::try_from(ctx.smt.get(width)).unwrap(); - - // Check that we haven't contradicted previous widths - if let Some(before_width) = w { - assert_eq!(*before_width, width_int as usize) - }; - - // Check that the width is nonzero - if width_int == 0 { - panic!("Unexpected, zero width! {t} {e:?}"); - } - - if unresolved_widths.contains(&width_name) { - log::debug!("\tResolved width: {width_name}, {width_int}"); - width_resolutions.insert(width_name, width_int); - cur_tyctx - .tymap - .insert(*t, Type::BitVector(Some(width_int as usize))); - } - } - } - let static_result = run_solver_with_static_widths(&rulectx, &cur_tyctx, concrete); - - // If we have a failure or unknown, return right away - if !matches!(static_result, VerificationResult::Success) { - return static_result; - } - - // Otherwise, try again, but adding the assertion that some width is - // different than our current assignment - let not_equals = width_resolutions.iter().map(|(s, w)| { - ctx.smt.not( - ctx.smt - .eq(ctx.smt.atom(s.clone()), ctx.smt.atom((*w).to_string())), - ) - }); - ctx.smt.assert(ctx.smt.or_many(not_equals)).unwrap(); - - resolve_dynamic_widths(rulectx, concrete, ctx, unresolved_widths, attempt + 1) - } - Ok(Response::Unsat) => { - if attempt == 0 { - log::warn!( - "Rule not applicable as written for rule assumptions, skipping full query" - ); - let unsat = ctx.smt.get_unsat_core().unwrap(); - log::warn!("Unsat core:\n{}", ctx.smt.display(unsat)); - VerificationResult::InapplicableRule - } else { - // If this is not the first attempt, some previous width assignment must - // have succeeded. - VerificationResult::Success - } - } - Ok(Response::Unknown) => { - panic!("Solver said 'unk'"); - } - Err(err) => { - unreachable!("Error! {:?}", err); - } - } -} - -pub fn run_solver_with_static_widths( - rulectx: &RuleCtx, - tyctx: &TypeContext, - concrete: &Option, -) -> VerificationResult { - // Declare variables again, this time with all static widths - let mut solver = easy_smt::ContextBuilder::new() - .replay_file(Some(std::fs::File::create("static_widths.smt2").unwrap())) - .solver("z3", ["-smt2", "-in"]) - .build() - .unwrap(); - solver - .set_option(":produce-unsat-cores", solver.true_()) - .unwrap(); - let mut ctx = SolverCtx { - smt: solver, - find_widths: false, - tyctx: tyctx.clone(), - bitwidth: MAX_WIDTH, - var_map: HashMap::new(), - width_vars: HashMap::new(), - width_assumptions: vec![], - additional_decls: vec![], - additional_assumptions: vec![], - additional_assertions: vec![], - fresh_bits_idx: 0, - lhs_load_args: None, - rhs_load_args: None, - lhs_store_args: None, - rhs_store_args: None, - load_return: None, - lhs_flag: true, - }; - let (assumptions, mut assertions) = ctx.declare_variables(rulectx.rule_sem, rulectx.config); - - let lhs = ctx.vir_expr_to_sexp(rulectx.rule_sem.lhs.clone()); - ctx.lhs_flag = false; - let rhs = ctx.vir_expr_to_sexp(rulectx.rule_sem.rhs.clone()); - - // For debugging - let unnamed_rule = String::from(""); - let rulename = rulectx - .rule - .name - .map(|name| &rulectx.typeenv.syms[name.index()]) - .unwrap_or(&unnamed_rule); - let unit = "()".to_string(); - let widthname = ctx - .static_width(&rulectx.rule_sem.lhs) - .map_or(unit, |s| format!("width {s}")); - - // Check whether the assumptions are possible - let feasibility = ctx.check_assumptions_feasibility( - &assumptions, - &rulectx.rule_sem.term_input_bvs, - rulectx.config, - ); - if feasibility != VerificationResult::Success { - log::warn!("Rule not applicable as written for rule assumptions, skipping full query"); - return feasibility; - } - - // Correctness query - // Verification condition: first rule's LHS and RHS are equal - if let Some(concrete) = concrete { - return test_concrete_with_static_widths( - rulectx, - concrete, - lhs, - rhs, - &mut ctx, - assumptions, - ); - } - - let condition = if let Some(condition) = &rulectx.config.custom_verification_condition { - let term_args = rulectx - .rule_sem - .term_args - .iter() - .map(|s| ctx.smt.atom(s)) - .collect(); - let custom_condition = condition(&ctx.smt, term_args, lhs, rhs); - log::debug!( - "Custom verification condition:\n\t{}\n", - ctx.smt.display(custom_condition) - ); - custom_condition - } else { - // Note: this is where we ask if the LHS and the RHS are equal - let side_equality = ctx.smt.eq(lhs, rhs); - log::debug!( - "LHS and RHS equality condition:{}", - ctx.smt.display(side_equality) - ); - side_equality - }; - - for a in &ctx.additional_assertions { - assertions.push(*a); - } - - let assumption_conjunction = ctx.smt.and_many(assumptions); - let mut full_condition = if !assertions.is_empty() { - let assertion_conjunction = ctx.smt.and_many(assertions.clone()); - ctx.smt.and(condition, assertion_conjunction) - } else { - condition - }; - - let mut load_conditions = vec![]; - match (&ctx.lhs_load_args, &ctx.rhs_load_args) { - (Some(_), Some(_)) => { - let lhs_args_vec = ctx.lhs_load_args.clone().unwrap(); - let rhs_args_vec = ctx.rhs_load_args.clone().unwrap(); - log::debug!("Load argument conditions:"); - for i in 0..lhs_args_vec.len() { - let arg_equal = ctx.smt.eq(lhs_args_vec[i], rhs_args_vec[i]); - load_conditions.push(arg_equal); - log::debug!("\t{}", ctx.smt.display(arg_equal)); - full_condition = ctx.smt.and(full_condition, arg_equal); - } - } - (None, None) => (), - (Some(_), None) => { - log::error!("Verification failed for {rulename}, {widthname}"); - log::error!("Left hand side has load statement but right hand side does not."); - return VerificationResult::Failure(Counterexample {}); - } - (None, Some(_)) => { - log::error!("Verification failed for {rulename}, {widthname}"); - log::error!("Right hand side has load statement but left hand side does not."); - return VerificationResult::Failure(Counterexample {}); - } - } - - let mut store_conditions = vec![]; - match (&ctx.lhs_store_args, &ctx.rhs_store_args) { - (Some(_), Some(_)) => { - let lhs_args_vec = ctx.lhs_store_args.clone().unwrap(); - let rhs_args_vec = ctx.rhs_store_args.clone().unwrap(); - log::debug!("Store argument conditions:"); - - for i in 0..lhs_args_vec.len() { - let arg_equal = ctx.smt.eq(lhs_args_vec[i], rhs_args_vec[i]); - store_conditions.push(arg_equal); - log::debug!("\t{}", ctx.smt.display(arg_equal)); - full_condition = ctx.smt.and(full_condition, arg_equal) - } - } - (None, None) => (), - (Some(_), None) => { - log::error!("Verification failed for {rulename}, {widthname}"); - log::error!("Left hand side has store statement but right hand side does not."); - return VerificationResult::Failure(Counterexample {}); - } - (None, Some(_)) => { - log::error!("Verification failed for {rulename}, {widthname}"); - log::error!("Right hand side has store statement but left hand side does not."); - return VerificationResult::Failure(Counterexample {}); - } - } - - log::trace!( - "Full verification condition:{}", - ctx.smt.display(full_condition) - ); - let query = ctx - .smt - .not(ctx.smt.imp(assumption_conjunction, full_condition)); - log::trace!("Running query"); - ctx.smt.assert(query).unwrap(); - - match ctx.smt.check() { - Ok(Response::Sat) => { - println!("Verification failed for {rulename}, {widthname}"); - ctx.display_model(rulectx.termenv, rulectx.typeenv, rulectx.rule, lhs, rhs); - let vals = ctx.smt.get_value(vec![condition]).unwrap(); - for (variable, value) in vals { - if value == ctx.smt.false_() { - println!("Failed condition:\n{}", ctx.smt.display(variable)); - } else if value == ctx.smt.true_() { - println!("Condition met, but failed some assertion(s).") - } - } - - if !assertions.is_empty() { - let vals = ctx.smt.get_value(assertions).unwrap(); - for (variable, value) in vals { - if value == ctx.smt.false_() { - println!("Failed assertion:\n{}", ctx.smt.display(variable)); - } - } - } - - if !load_conditions.is_empty() { - let vals = ctx.smt.get_value(load_conditions).unwrap(); - for (variable, value) in vals { - if value == ctx.smt.false_() { - log::error!("Failed load condition:\n{}", ctx.smt.display(variable)); - } - } - } - VerificationResult::Failure(Counterexample {}) - } - Ok(Response::Unsat) => { - println!("Verification succeeded for {rulename}, {widthname}"); - VerificationResult::Success - } - Ok(Response::Unknown) => { - panic!("Solver said 'unk'"); - } - Err(err) => { - unreachable!("Error! {:?}", err); - } - } -} - -pub fn test_concrete_with_static_widths( - rulectx: &RuleCtx, - concrete: &ConcreteTest, - lhs: SExpr, - rhs: SExpr, - ctx: &mut SolverCtx, - assumptions: Vec, -) -> VerificationResult { - // Test code only: test against concrete input/output - // Check that our expected output is valid - for (i, a) in assumptions.iter().enumerate() { - ctx.smt - .assert(ctx.smt.named(format!("conc{i}"), *a)) - .unwrap(); - } - for (i, e) in ctx.additional_assertions.iter().enumerate() { - ctx.smt - .assert(ctx.smt.named(format!("conc_assert{i}"), *e)) - .unwrap(); - } - ctx.smt.push().unwrap(); - let eq = ctx - .smt - .eq(rhs, ctx.smt.atom(concrete.output.literal.clone())); - - ctx.smt - .assert(ctx.smt.named("conceq".to_string(), eq)) - .unwrap(); - - for (i, a) in rulectx.rule_sem.rhs_assertions.iter().enumerate() { - let p = ctx.vir_expr_to_sexp(a.clone()); - ctx.smt - .assert(ctx.smt.named(format!("rhs_assert{i}"), p)) - .unwrap(); - } - - if !matches!(ctx.smt.check(), Ok(Response::Sat)) { - // Bad! This is a bug! - // Pop the output assertion - ctx.smt.pop().unwrap(); - // Try again - assert!(matches!(ctx.smt.check(), Ok(Response::Sat))); - // Get the value for what output is to panic with a useful message - let val = ctx.smt.get_value(vec![rhs]).unwrap()[0].1; - ctx.display_model(rulectx.termenv, rulectx.typeenv, rulectx.rule, lhs, rhs); - panic!( - "Expected {}, got {}", - concrete.output.literal, - ctx.display_hex_to_bin(val) - ); - } else { - log::debug!( - "Expected concrete result matched: {}", - concrete.output.literal - ); - ctx.smt.pop().unwrap(); - } - - // Check that there is no other possible output - ctx.smt.push().unwrap(); - ctx.smt - .assert( - ctx.smt.not( - ctx.smt - .eq(rhs, ctx.smt.atom(concrete.output.literal.clone())), - ), - ) - .unwrap(); - if !matches!(ctx.smt.check(), Ok(Response::Unsat)) { - // Get the value for what output is to panic with a useful message - let val = ctx.smt.get_value(vec![rhs]).unwrap()[0].1; - ctx.display_model(rulectx.termenv, rulectx.typeenv, rulectx.rule, lhs, rhs); - // AVH TODO: should probably elevate back to an error with custom verification condition - log::error!( - "WARNING: Expected ONLY {}, got POSSIBLE {}", - concrete.output.literal, - ctx.display_hex_to_bin(val) - ); - } - ctx.smt.pop().unwrap(); - VerificationResult::Success -} diff --git a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/cls.rs b/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/cls.rs deleted file mode 100644 index 6f0a94d80b6b..000000000000 --- a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/cls.rs +++ /dev/null @@ -1,3501 +0,0 @@ -use crate::solver::SolverCtx; -use easy_smt::SExpr; - -// Future work: possibly move these into the annotation language or an SMTLIB prelude -// Adapted from https://stackoverflow.com/questions/23856596/how-to-count-leading-zeros-in-a-32-bit-unsigned-integer - -pub fn cls64(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - // Generated code. - // total zeros counter - let zret0 = solver.declare( - format!("zret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - )); - // round 1 - let zret1 = solver.declare( - format!("zret1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zy32 = solver.declare( - format!("zy32_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zx32 = solver.declare( - format!("zx32_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - zy32, - solver.smt.bvlshr(x, solver.smt.atom("#x0000000000000020")), - )); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy32, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zret1, zret0), - solver.smt.eq( - zret1, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv32"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy32, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zx32, zy32), - solver.smt.eq(zx32, x), - ])); - // round 2 - let zret2 = solver.declare( - format!("zret2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zy16 = solver.declare( - format!("zy16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zx16 = solver.declare( - format!("zx16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - zy16, - solver - .smt - .bvlshr(zx32, solver.smt.atom("#x0000000000000010")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zret2, zret1), - solver.smt.eq( - zret2, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv16"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zx16, zy16), - solver.smt.eq(zx16, zx32), - ])); - // round 3 - let zret3 = solver.declare( - format!("zret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zy8 = solver.declare( - format!("zy8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zx8 = solver.declare( - format!("zx8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - zy8, - solver - .smt - .bvlshr(zx16, solver.smt.atom("#x0000000000000008")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zret3, zret2), - solver.smt.eq( - zret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zx8, zy8), - solver.smt.eq(zx8, zx16), - ])); - // round 4 - let zret4 = solver.declare( - format!("zret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zy4 = solver.declare( - format!("zy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zx4 = solver.declare( - format!("zx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - zy4, - solver - .smt - .bvlshr(zx8, solver.smt.atom("#x0000000000000004")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zret4, zret3), - solver.smt.eq( - zret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zx4, zy4), - solver.smt.eq(zx4, zx8), - ])); - // round 5 - let zret5 = solver.declare( - format!("zret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zy2 = solver.declare( - format!("zy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zx2 = solver.declare( - format!("zx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - zy2, - solver - .smt - .bvlshr(zx4, solver.smt.atom("#x0000000000000002")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zret5, zret4), - solver.smt.eq( - zret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zx2, zy2), - solver.smt.eq(zx2, zx4), - ])); - // round 6 - let zret6 = solver.declare( - format!("zret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zy1 = solver.declare( - format!("zy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let zx1 = solver.declare( - format!("zx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - zy1, - solver - .smt - .bvlshr(zx2, solver.smt.atom("#x0000000000000001")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zret6, zret5), - solver.smt.eq( - zret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zx1, zy1), - solver.smt.eq(zx1, zx2), - ])); - // last round - let zret7 = solver.declare( - format!("zret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(zret7, zret6), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - let clzret = solver.declare( - format!("clzret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - solver.smt.eq(clzret, zret7), - solver.smt.eq( - clzret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - // total zeros counter - let sret0 = solver.declare( - format!("sret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - )); - // round 1 - let sret1 = solver.declare( - format!("sret1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sy32 = solver.declare( - format!("sy32_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sx32 = solver.declare( - format!("sx32_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - sy32, - solver.smt.bvashr(x, solver.smt.atom("#x0000000000000020")), - )); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy32, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sret1, sret0), - solver.smt.eq( - sret1, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv32"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy32, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sx32, sy32), - solver.smt.eq(sx32, x), - ])); - // round 2 - let sret2 = solver.declare( - format!("sret2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sy16 = solver.declare( - format!("sy16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sx16 = solver.declare( - format!("sx16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - sy16, - solver - .smt - .bvashr(sx32, solver.smt.atom("#x0000000000000010")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sret2, sret1), - solver.smt.eq( - sret2, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv16"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sx16, sy16), - solver.smt.eq(sx16, sx32), - ])); - // round 3 - let sret3 = solver.declare( - format!("sret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sy8 = solver.declare( - format!("sy8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sx8 = solver.declare( - format!("sx8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - sy8, - solver - .smt - .bvashr(sx16, solver.smt.atom("#x0000000000000008")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sret3, sret2), - solver.smt.eq( - sret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sx8, sy8), - solver.smt.eq(sx8, sx16), - ])); - // round 4 - let sret4 = solver.declare( - format!("sret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sy4 = solver.declare( - format!("sy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sx4 = solver.declare( - format!("sx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - sy4, - solver - .smt - .bvashr(sx8, solver.smt.atom("#x0000000000000004")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sret4, sret3), - solver.smt.eq( - sret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sx4, sy4), - solver.smt.eq(sx4, sx8), - ])); - // round 5 - let sret5 = solver.declare( - format!("sret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sy2 = solver.declare( - format!("sy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sx2 = solver.declare( - format!("sx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - sy2, - solver - .smt - .bvashr(sx4, solver.smt.atom("#x0000000000000002")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sret5, sret4), - solver.smt.eq( - sret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sx2, sy2), - solver.smt.eq(sx2, sx4), - ])); - // round 6 - let sret6 = solver.declare( - format!("sret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sy1 = solver.declare( - format!("sy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let sx1 = solver.declare( - format!("sx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - sy1, - solver - .smt - .bvashr(sx2, solver.smt.atom("#x0000000000000001")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sret6, sret5), - solver.smt.eq( - sret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sx1, sy1), - solver.smt.eq(sx1, sx2), - ])); - // last round - let sret7 = solver.declare( - format!("sret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv18446744073709551615"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(sret7, sret6), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - let clsret = solver.declare( - format!("clsret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - solver.smt.eq(clsret, sret7), - solver.smt.eq( - clsret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - let cls64ret = solver.declare( - format!("cls64ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("bvsle"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - x, - ]), - solver.smt.eq(cls64ret, clzret), - solver.smt.eq(cls64ret, clsret), - ])); - - cls64ret -} - -pub fn cls32(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(31, 0, x); - - // Generated code. - // total zeros counter - let zret0 = solver.declare( - format!("zret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - )); - // round 1 - let zret2 = solver.declare( - format!("zret2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zy16 = solver.declare( - format!("zy16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zx16 = solver.declare( - format!("zx16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(zy16, solver.smt.bvlshr(x, solver.smt.atom("#x00000010"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zret2, zret0), - solver.smt.eq( - zret2, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv16"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zx16, zy16), - solver.smt.eq(zx16, x), - ])); - // round 2 - let zret3 = solver.declare( - format!("zret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zy8 = solver.declare( - format!("zy8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zx8 = solver.declare( - format!("zx8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(zy8, solver.smt.bvlshr(zx16, solver.smt.atom("#x00000008"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zret3, zret2), - solver.smt.eq( - zret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zx8, zy8), - solver.smt.eq(zx8, zx16), - ])); - // round 3 - let zret4 = solver.declare( - format!("zret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zy4 = solver.declare( - format!("zy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zx4 = solver.declare( - format!("zx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(zy4, solver.smt.bvlshr(zx8, solver.smt.atom("#x00000004"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zret4, zret3), - solver.smt.eq( - zret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zx4, zy4), - solver.smt.eq(zx4, zx8), - ])); - // round 4 - let zret5 = solver.declare( - format!("zret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zy2 = solver.declare( - format!("zy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zx2 = solver.declare( - format!("zx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(zy2, solver.smt.bvlshr(zx4, solver.smt.atom("#x00000002"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zret5, zret4), - solver.smt.eq( - zret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zx2, zy2), - solver.smt.eq(zx2, zx4), - ])); - // round 5 - let zret6 = solver.declare( - format!("zret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zy1 = solver.declare( - format!("zy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let zx1 = solver.declare( - format!("zx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(zy1, solver.smt.bvlshr(zx2, solver.smt.atom("#x00000001"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zret6, zret5), - solver.smt.eq( - zret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zx1, zy1), - solver.smt.eq(zx1, zx2), - ])); - // last round - let zret7 = solver.declare( - format!("zret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(zret7, zret6), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - let clzret = solver.declare( - format!("clzret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - solver.smt.eq(clzret, zret7), - solver.smt.eq( - clzret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - // total zeros counter - let sret0 = solver.declare( - format!("sret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - )); - // round 1 - let sret2 = solver.declare( - format!("sret2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sy16 = solver.declare( - format!("sy16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sx16 = solver.declare( - format!("sx16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(sy16, solver.smt.bvashr(x, solver.smt.atom("#x00000010"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sret2, sret0), - solver.smt.eq( - sret2, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv16"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sx16, sy16), - solver.smt.eq(sx16, x), - ])); - // round 2 - let sret3 = solver.declare( - format!("sret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sy8 = solver.declare( - format!("sy8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sx8 = solver.declare( - format!("sx8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(sy8, solver.smt.bvashr(sx16, solver.smt.atom("#x00000008"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sret3, sret2), - solver.smt.eq( - sret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sx8, sy8), - solver.smt.eq(sx8, sx16), - ])); - // round 3 - let sret4 = solver.declare( - format!("sret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sy4 = solver.declare( - format!("sy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sx4 = solver.declare( - format!("sx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(sy4, solver.smt.bvashr(sx8, solver.smt.atom("#x00000004"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sret4, sret3), - solver.smt.eq( - sret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sx4, sy4), - solver.smt.eq(sx4, sx8), - ])); - // round 4 - let sret5 = solver.declare( - format!("sret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sy2 = solver.declare( - format!("sy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sx2 = solver.declare( - format!("sx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(sy2, solver.smt.bvashr(sx4, solver.smt.atom("#x00000002"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sret5, sret4), - solver.smt.eq( - sret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sx2, sy2), - solver.smt.eq(sx2, sx4), - ])); - // round 5 - let sret6 = solver.declare( - format!("sret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sy1 = solver.declare( - format!("sy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let sx1 = solver.declare( - format!("sx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(sy1, solver.smt.bvashr(sx2, solver.smt.atom("#x00000001"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sret6, sret5), - solver.smt.eq( - sret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sx1, sy1), - solver.smt.eq(sx1, sx2), - ])); - // last round - let sret7 = solver.declare( - format!("sret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4294967295"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(sret7, sret6), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - let clsret = solver.declare( - format!("clsret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - solver.smt.eq(clsret, sret7), - solver.smt.eq( - clsret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - let cls32ret = solver.declare( - format!("cls32ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("bvsle"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - x, - ]), - solver.smt.eq(cls32ret, clzret), - solver.smt.eq(cls32ret, clsret), - ])); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 32); - solver.smt.concat(padding, cls32ret) - } else { - cls32ret - } -} - -pub fn cls16(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(15, 0, x); - - // Generated code. - // total zeros counter - let zret0 = solver.declare( - format!("zret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.eq( - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - )); - // round 1 - let zret3 = solver.declare( - format!("zret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zy8 = solver.declare( - format!("zy8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zx8 = solver.declare( - format!("zx8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(zy8, solver.smt.bvlshr(x, solver.smt.atom("#x0008"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zret3, zret0), - solver.smt.eq( - zret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zx8, zy8), - solver.smt.eq(zx8, x), - ])); - // round 2 - let zret4 = solver.declare( - format!("zret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zy4 = solver.declare( - format!("zy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zx4 = solver.declare( - format!("zx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(zy4, solver.smt.bvlshr(zx8, solver.smt.atom("#x0004"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zret4, zret3), - solver.smt.eq( - zret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zx4, zy4), - solver.smt.eq(zx4, zx8), - ])); - // round 3 - let zret5 = solver.declare( - format!("zret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zy2 = solver.declare( - format!("zy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zx2 = solver.declare( - format!("zx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(zy2, solver.smt.bvlshr(zx4, solver.smt.atom("#x0002"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zret5, zret4), - solver.smt.eq( - zret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zx2, zy2), - solver.smt.eq(zx2, zx4), - ])); - // round 4 - let zret6 = solver.declare( - format!("zret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zy1 = solver.declare( - format!("zy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let zx1 = solver.declare( - format!("zx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(zy1, solver.smt.bvlshr(zx2, solver.smt.atom("#x0001"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zret6, zret5), - solver.smt.eq( - zret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zx1, zy1), - solver.smt.eq(zx1, zx2), - ])); - // last round - let zret7 = solver.declare( - format!("zret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(zret7, zret6), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - let clzret = solver.declare( - format!("clzret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - solver.smt.eq(clzret, zret7), - solver.smt.eq( - clzret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - // total zeros counter - let sret0 = solver.declare( - format!("sret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.eq( - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - )); - // round 1 - let sret3 = solver.declare( - format!("sret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sy8 = solver.declare( - format!("sy8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sx8 = solver.declare( - format!("sx8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(sy8, solver.smt.bvashr(x, solver.smt.atom("#x0008"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sret3, sret0), - solver.smt.eq( - sret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sx8, sy8), - solver.smt.eq(sx8, x), - ])); - // round 2 - let sret4 = solver.declare( - format!("sret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sy4 = solver.declare( - format!("sy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sx4 = solver.declare( - format!("sx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(sy4, solver.smt.bvashr(sx8, solver.smt.atom("#x0004"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sret4, sret3), - solver.smt.eq( - sret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sx4, sy4), - solver.smt.eq(sx4, sx8), - ])); - // round 3 - let sret5 = solver.declare( - format!("sret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sy2 = solver.declare( - format!("sy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sx2 = solver.declare( - format!("sx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(sy2, solver.smt.bvashr(sx4, solver.smt.atom("#x0002"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sret5, sret4), - solver.smt.eq( - sret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sx2, sy2), - solver.smt.eq(sx2, sx4), - ])); - // round 4 - let sret6 = solver.declare( - format!("sret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sy1 = solver.declare( - format!("sy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let sx1 = solver.declare( - format!("sx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(sy1, solver.smt.bvashr(sx2, solver.smt.atom("#x0001"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sret6, sret5), - solver.smt.eq( - sret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sx1, sy1), - solver.smt.eq(sx1, sx2), - ])); - // last round - let sret7 = solver.declare( - format!("sret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv65535"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(sret7, sret6), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - let clsret = solver.declare( - format!("clsret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - solver.smt.eq(clsret, sret7), - solver.smt.eq( - clsret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - let cls16ret = solver.declare( - format!("cls16ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("bvsle"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - x, - ]), - solver.smt.eq(cls16ret, clzret), - solver.smt.eq(cls16ret, clsret), - ])); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 16); - solver.smt.concat(padding, cls16ret) - } else { - cls16ret - } -} - -pub fn cls8(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(7, 0, x); - - // Generated code. - // total zeros counter - let zret0 = solver.declare( - format!("zret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.eq( - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - )); - // round 1 - let zret4 = solver.declare( - format!("zret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let zy4 = solver.declare( - format!("zy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let zx4 = solver.declare( - format!("zx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(zy4, solver.smt.bvlshr(x, solver.smt.atom("#x04"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(zret4, zret0), - solver.smt.eq( - zret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(zx4, zy4), - solver.smt.eq(zx4, x), - ])); - // round 2 - let zret5 = solver.declare( - format!("zret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let zy2 = solver.declare( - format!("zy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let zx2 = solver.declare( - format!("zx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(zy2, solver.smt.bvlshr(zx4, solver.smt.atom("#x02"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(zret5, zret4), - solver.smt.eq( - zret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(zx2, zy2), - solver.smt.eq(zx2, zx4), - ])); - // round 3 - let zret6 = solver.declare( - format!("zret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let zy1 = solver.declare( - format!("zy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let zx1 = solver.declare( - format!("zx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(zy1, solver.smt.bvlshr(zx2, solver.smt.atom("#x01"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(zret6, zret5), - solver.smt.eq( - zret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(zx1, zy1), - solver.smt.eq(zx1, zx2), - ])); - // last round - let zret7 = solver.declare( - format!("zret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - zx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(zret7, zret6), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - zret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - let clzret = solver.declare( - format!("clzret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - solver.smt.eq(clzret, zret7), - solver.smt.eq( - clzret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - zret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - // total zeros counter - let sret0 = solver.declare( - format!("sret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.eq( - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - )); - // round 1 - let sret4 = solver.declare( - format!("sret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let sy4 = solver.declare( - format!("sy4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let sx4 = solver.declare( - format!("sx4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(sy4, solver.smt.bvashr(x, solver.smt.atom("#x04"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv255"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(sret4, sret0), - solver.smt.eq( - sret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv255"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(sx4, sy4), - solver.smt.eq(sx4, x), - ])); - // round 2 - let sret5 = solver.declare( - format!("sret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let sy2 = solver.declare( - format!("sy2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let sx2 = solver.declare( - format!("sx2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(sy2, solver.smt.bvashr(sx4, solver.smt.atom("#x02"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv255"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(sret5, sret4), - solver.smt.eq( - sret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv255"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(sx2, sy2), - solver.smt.eq(sx2, sx4), - ])); - // round 3 - let sret6 = solver.declare( - format!("sret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let sy1 = solver.declare( - format!("sy1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let sx1 = solver.declare( - format!("sx1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(sy1, solver.smt.bvashr(sx2, solver.smt.atom("#x01"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv255"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(sret6, sret5), - solver.smt.eq( - sret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sy1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv255"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(sx1, sy1), - solver.smt.eq(sx1, sx2), - ])); - // last round - let sret7 = solver.declare( - format!("sret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - sx1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv255"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(sret7, sret6), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - sret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - let clsret = solver.declare( - format!("clsret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.eq( - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - solver.smt.eq(clsret, sret7), - solver.smt.eq( - clsret, - solver.smt.list(vec![ - solver.smt.atom("bvsub"), - sret7, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - let cls8ret = solver.declare( - format!("cls8ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("bvsle"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - x, - ]), - solver.smt.eq(cls8ret, clzret), - solver.smt.eq(cls8ret, clsret), - ])); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 8); - solver.smt.concat(padding, cls8ret) - } else { - cls8ret - } -} - -pub fn cls1(solver: &mut SolverCtx, id: u32) -> SExpr { - // Generated code. - let cls1ret = solver.declare( - format!("cls1ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(1), - ]), - ); - solver.assume(solver.smt.eq( - cls1ret, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(1), - ]), - )); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 1); - solver.smt.concat(padding, cls1ret) - } else { - cls1ret - } -} diff --git a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/clz.rs b/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/clz.rs deleted file mode 100644 index ff5ce0f9553c..000000000000 --- a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/clz.rs +++ /dev/null @@ -1,1607 +0,0 @@ -use crate::solver::SolverCtx; -use easy_smt::SExpr; - -// Future work: possibly move these into the annotation language or an SMTLIB prelude -// Adapted from https://stackoverflow.com/questions/23856596/how-to-count-leading-zeros-in-a-32-bit-unsigned-integer - -pub fn clz64(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - // Generated code. - // total zeros counter - let ret0 = solver.declare( - format!("ret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - ret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - )); - // round 1 - let ret1 = solver.declare( - format!("ret1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let y32 = solver.declare( - format!("y32_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let x32 = solver.declare( - format!("x32_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - y32, - solver.smt.bvlshr(x, solver.smt.atom("#x0000000000000020")), - )); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y32, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(ret1, ret0), - solver.smt.eq( - ret1, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv32"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y32, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(x32, y32), - solver.smt.eq(x32, x), - ])); - // round 2 - let ret2 = solver.declare( - format!("ret2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let y16 = solver.declare( - format!("y16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let x16 = solver.declare( - format!("x16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - y16, - solver - .smt - .bvlshr(x32, solver.smt.atom("#x0000000000000010")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(ret2, ret1), - solver.smt.eq( - ret2, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv16"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(x16, y16), - solver.smt.eq(x16, x32), - ])); - // round 3 - let ret3 = solver.declare( - format!("ret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let y8 = solver.declare( - format!("y8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let x8 = solver.declare( - format!("x8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume( - solver.smt.eq( - y8, - solver - .smt - .bvlshr(x16, solver.smt.atom("#x0000000000000008")), - ), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(ret3, ret2), - solver.smt.eq( - ret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(x8, y8), - solver.smt.eq(x8, x16), - ])); - // round 4 - let ret4 = solver.declare( - format!("ret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let y4 = solver.declare( - format!("y4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let x4 = solver.declare( - format!("x4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - y4, - solver.smt.bvlshr(x8, solver.smt.atom("#x0000000000000004")), - )); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(ret4, ret3), - solver.smt.eq( - ret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(x4, y4), - solver.smt.eq(x4, x8), - ])); - // round 5 - let ret5 = solver.declare( - format!("ret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let y2 = solver.declare( - format!("y2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - y2, - solver.smt.bvlshr(x4, solver.smt.atom("#x0000000000000002")), - )); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(ret5, ret4), - solver.smt.eq( - ret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(x2, y2), - solver.smt.eq(x2, x4), - ])); - // round 6 - let ret6 = solver.declare( - format!("ret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let y1 = solver.declare( - format!("y1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - y1, - solver.smt.bvlshr(x2, solver.smt.atom("#x0000000000000001")), - )); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(ret6, ret5), - solver.smt.eq( - ret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(x1, y1), - solver.smt.eq(x1, x2), - ])); - - // last round - let ret7 = solver.declare( - format!("ret7_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - x1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(64), - ]), - ), - ]), - solver.smt.eq(ret7, ret6), - solver.smt.eq( - ret7, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret6, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(64), - ]), - ]), - ), - ])); - - ret7 -} - -pub fn clz32(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(31, 0, x); - - // Generated code. - // total zeros counter - let ret0 = solver.declare( - format!("ret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - ret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - )); - // round 1 - let ret1 = solver.declare( - format!("ret1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let y16 = solver.declare( - format!("y16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let x16 = solver.declare( - format!("x16_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(y16, solver.smt.bvlshr(x, solver.smt.atom("#x00000010"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(ret1, ret0), - solver.smt.eq( - ret1, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv16"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y16, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(x16, y16), - solver.smt.eq(x16, x), - ])); - // round 2 - let ret2 = solver.declare( - format!("ret2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let y8 = solver.declare( - format!("y8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let x8 = solver.declare( - format!("x8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(y8, solver.smt.bvlshr(x16, solver.smt.atom("#x00000008"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(ret2, ret1), - solver.smt.eq( - ret2, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(x8, y8), - solver.smt.eq(x8, x16), - ])); - // round 3 - let ret3 = solver.declare( - format!("ret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let y4 = solver.declare( - format!("y4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let x4 = solver.declare( - format!("x4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(y4, solver.smt.bvlshr(x8, solver.smt.atom("#x00000004"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(ret3, ret2), - solver.smt.eq( - ret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(x4, y4), - solver.smt.eq(x4, x8), - ])); - // round 4 - let ret4 = solver.declare( - format!("ret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let y2 = solver.declare( - format!("y2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(y2, solver.smt.bvlshr(x4, solver.smt.atom("#x00000002"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(ret4, ret3), - solver.smt.eq( - ret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(x2, y2), - solver.smt.eq(x2, x4), - ])); - // round 5 - let ret5 = solver.declare( - format!("ret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let y1 = solver.declare( - format!("y1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume( - solver - .smt - .eq(y1, solver.smt.bvlshr(x2, solver.smt.atom("#x00000001"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(ret5, ret4), - solver.smt.eq( - ret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(x1, y1), - solver.smt.eq(x1, x2), - ])); - - // last round - let ret6 = solver.declare( - format!("ret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - x1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(32), - ]), - ), - ]), - solver.smt.eq(ret6, ret5), - solver.smt.eq( - ret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(32), - ]), - ]), - ), - ])); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 32); - solver.smt.concat(padding, ret6) - } else { - ret6 - } -} - -pub fn clz16(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(15, 0, x); - - // Generated code. - // total zeros counter - let ret1 = solver.declare( - format!("ret1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.eq( - ret1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - )); - // round 1 - let ret2 = solver.declare( - format!("ret2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let y8 = solver.declare( - format!("y8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let x8 = solver.declare( - format!("x8_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(y8, solver.smt.bvlshr(x, solver.smt.atom("#x0008"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(ret2, ret1), - solver.smt.eq( - ret2, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv8"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y8, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(x8, y8), - solver.smt.eq(x8, x), - ])); - // round 2 - let ret3 = solver.declare( - format!("ret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let y4 = solver.declare( - format!("y4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let x4 = solver.declare( - format!("x4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(y4, solver.smt.bvlshr(x8, solver.smt.atom("#x0004"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(ret3, ret2), - solver.smt.eq( - ret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(x4, y4), - solver.smt.eq(x4, x8), - ])); - // round 3 - let ret4 = solver.declare( - format!("ret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let y2 = solver.declare( - format!("y2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(y2, solver.smt.bvlshr(x4, solver.smt.atom("#x0002"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(ret4, ret3), - solver.smt.eq( - ret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(x2, y2), - solver.smt.eq(x2, x4), - ])); - // round 4 - let ret5 = solver.declare( - format!("ret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let y1 = solver.declare( - format!("y1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume( - solver - .smt - .eq(y1, solver.smt.bvlshr(x2, solver.smt.atom("#x0001"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(ret5, ret4), - solver.smt.eq( - ret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(x1, y1), - solver.smt.eq(x1, x2), - ])); - - // last round - let ret6 = solver.declare( - format!("ret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - x1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(16), - ]), - ), - ]), - solver.smt.eq(ret6, ret5), - solver.smt.eq( - ret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(16), - ]), - ]), - ), - ])); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 16); - solver.smt.concat(padding, ret6) - } else { - ret6 - } -} - -pub fn clz8(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(7, 0, x); - - // Generated code. - // total zeros counter - let ret0 = solver.declare( - format!("ret0_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.eq( - ret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - )); - // round 1 - let ret3 = solver.declare( - format!("ret3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let y4 = solver.declare( - format!("y4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let x4 = solver.declare( - format!("x4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(y4, solver.smt.bvlshr(x, solver.smt.atom("#x04"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(ret3, ret0), - solver.smt.eq( - ret3, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret0, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv4"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(x4, y4), - solver.smt.eq(x4, x), - ])); - // round 2 - let ret4 = solver.declare( - format!("ret4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let y2 = solver.declare( - format!("y2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(y2, solver.smt.bvlshr(x4, solver.smt.atom("#x02"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(ret4, ret3), - solver.smt.eq( - ret4, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret3, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv2"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y2, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(x2, y2), - solver.smt.eq(x2, x4), - ])); - // round 3 - let ret5 = solver.declare( - format!("ret5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let y1 = solver.declare( - format!("y1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume( - solver - .smt - .eq(y1, solver.smt.bvlshr(x2, solver.smt.atom("#x01"))), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(ret5, ret4), - solver.smt.eq( - ret5, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret4, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - y1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(x1, y1), - solver.smt.eq(x1, x2), - ])); - // last round - let ret6 = solver.declare( - format!("ret6_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.list(vec![ - solver.smt.atom("ite"), - solver.smt.list(vec![ - solver.smt.atom("not"), - solver.smt.eq( - x1, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv0"), - solver.smt.numeral(8), - ]), - ), - ]), - solver.smt.eq(ret6, ret5), - solver.smt.eq( - ret6, - solver.smt.list(vec![ - solver.smt.atom("bvadd"), - ret5, - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("bv1"), - solver.smt.numeral(8), - ]), - ]), - ), - ])); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 8); - solver.smt.concat(padding, ret6) - } else { - ret6 - } -} - -pub fn clz1(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(0, 0, x); - - // Generated code. - let clz1ret = solver.declare( - format!("clz1ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(1), - ]), - ); - solver.assume( - solver - .smt - .eq(clz1ret, solver.smt.list(vec![solver.smt.atom("bvnot"), x])), - ); - - if solver.find_widths { - let padding = solver.new_fresh_bits(solver.bitwidth - 1); - solver.smt.concat(padding, clz1ret) - } else { - clz1ret - } -} diff --git a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/mod.rs b/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/mod.rs deleted file mode 100644 index 44c4a9cbd445..000000000000 --- a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/mod.rs +++ /dev/null @@ -1,444 +0,0 @@ -pub mod cls; -pub mod clz; -pub mod popcnt; -pub mod rev; -pub mod subs; - -#[cfg(test)] -mod tests { - use super::*; - use crate::solver::SolverCtx; - use easy_smt::{Response, SExpr}; - use std::collections::HashMap; - use veri_ir::TypeContext; - - fn get_ctx() -> SolverCtx { - let smt = easy_smt::ContextBuilder::new() - .replay_file(Some(std::fs::File::create("encoding_tests.smt2").unwrap())) - .solver("z3", ["-smt2", "-in"]) - .build() - .unwrap(); - SolverCtx { - smt, - find_widths: false, - tyctx: TypeContext { - tyvars: HashMap::new(), - tymap: HashMap::new(), - tyvals: HashMap::new(), - bv_unknown_width_sets: HashMap::new(), - }, - bitwidth: 64, - var_map: HashMap::new(), - width_vars: HashMap::new(), - width_assumptions: vec![], - additional_decls: vec![], - additional_assumptions: vec![], - additional_assertions: vec![], - fresh_bits_idx: 0, - lhs_load_args: None, - rhs_load_args: None, - lhs_store_args: None, - rhs_store_args: None, - lhs_flag: true, - load_return: None, - } - } - - /// Check that the solver encoding meets expectations for the given input and output. - /// Right now, only works for encodings with a single argument that return a value with - /// the same width as the input. - /// Check that the output is equal to the expected output, and no other output is possible. - fn check_unary_encoding_with_solver(encoding: &str, input: &str, output: &str, width: usize) { - let mut ctx = get_ctx(); - - // Set up an input variable - let ty = ctx.smt.bit_vec_sort(ctx.smt.numeral(width)); - let input_var = ctx.declare("input".to_string(), ty); - - // Set the input equal to our expected input - ctx.additional_assumptions - .push(ctx.smt.eq(input_var, ctx.smt.atom(input))); - - // Call the encoding function to be tested - let output_from_call = match (encoding, width) { - ("rev", 8) => rev::rev8(&mut ctx, input_var, 0), - ("rev", 16) => rev::rev16(&mut ctx, input_var, 0), - ("rev", 32) => rev::rev32(&mut ctx, input_var, 0), - ("rev", 64) => rev::rev64(&mut ctx, input_var, 0), - - ("clz", 8) => clz::clz8(&mut ctx, input_var, 0), - ("clz", 16) => clz::clz16(&mut ctx, input_var, 0), - ("clz", 32) => clz::clz32(&mut ctx, input_var, 0), - ("clz", 64) => clz::clz64(&mut ctx, input_var, 0), - - ("cls", 8) => cls::cls8(&mut ctx, input_var, 0), - ("cls", 16) => cls::cls16(&mut ctx, input_var, 0), - ("cls", 32) => cls::cls32(&mut ctx, input_var, 0), - ("cls", 64) => cls::cls64(&mut ctx, input_var, 0), - - ("popcnt", ty) => popcnt::popcnt(&mut ctx, ty, input_var, 0), - _ => unreachable!(), - }; - check_encoding_with_solver(ctx, output_from_call, output, width) - } - - fn check_encoding_with_solver(mut ctx: SolverCtx, call: SExpr, output: &str, width: usize) { - // Extract the width of bits that we care about. - let output_care_bits = ctx.smt.extract((width - 1).try_into().unwrap(), 0, call); - ctx.smt.display(output_care_bits).to_string(); - - // Bookkeeping: declare declarations, assert assumptions - for (name, ty) in &ctx.additional_decls { - ctx.smt.declare_const(name, *ty).unwrap(); - } - if ctx.additional_assumptions.len() > 1 { - ctx.smt - .assert(ctx.smt.and_many(ctx.additional_assumptions.clone())) - .unwrap(); - } - - // Check that our expected output is valid - ctx.smt.push().unwrap(); - ctx.smt - .assert(ctx.smt.eq(output_care_bits, ctx.smt.atom(output))) - .unwrap(); - if !matches!(ctx.smt.check(), Ok(Response::Sat)) { - // Bad! This is a bug! - // Pop the output assertion - ctx.smt.pop().unwrap(); - // Try again - assert!(matches!(ctx.smt.check(), Ok(Response::Sat))); - - let model = ctx.smt.get_model().unwrap(); - println!("{}", ctx.smt.display(model)); - - // Get the value for what output is to panic with a useful message - let val = ctx.smt.get_value(vec![output_care_bits]).unwrap()[0].1; - - panic!("Expected {}, got {}", output, ctx.display_hex_to_bin(val)); - } else { - ctx.smt.pop().unwrap(); - } - - // Check that there is no other possible output - ctx.smt.push().unwrap(); - ctx.smt - .assert( - ctx.smt - .not(ctx.smt.eq(output_care_bits, ctx.smt.atom(output))), - ) - .unwrap(); - if !matches!(ctx.smt.check(), Ok(Response::Unsat)) { - let model = ctx.smt.get_model().unwrap(); - println!("{}", ctx.smt.display(model)); - - // Get the value for what output is to panic with a useful message - let val = ctx.smt.get_value(vec![output_care_bits]).unwrap()[0].1; - panic!( - "Multiple possible outputs! Expected only {}, got {}", - output, - ctx.display_hex_to_bin(val) - ); - } - ctx.smt.pop().unwrap(); - } - - fn check(ctx: &SolverCtx, expr: SExpr, expected: &str) { - let expr_s = format!("{}", ctx.smt.display(expr)); - assert_eq!(expr_s, expected); - } - - #[test] - fn rev1_test() { - let mut ctx = get_ctx(); - - let x = ctx.smt.atom("x"); - let res = rev::rev1(&mut ctx, x, 42); - - check(&ctx, res, "(concat fresh0 rev1ret_42)"); - check(&ctx, ctx.additional_decls[0].1, "(_ BitVec 1)"); - check( - &ctx, - ctx.additional_assumptions[0], - "(= rev1ret_42 ((_ extract 0 0) x))", - ); - } - - #[test] - fn test_rev8_with_solver() { - check_unary_encoding_with_solver("rev", "#b01010101", "#b10101010", 8); - check_unary_encoding_with_solver("rev", "#b11110000", "#b00001111", 8); - check_unary_encoding_with_solver("rev", "#b00000000", "#b00000000", 8); - check_unary_encoding_with_solver("rev", "#b11111111", "#b11111111", 8); - } - - #[test] - fn test_rev16_with_solver() { - check_unary_encoding_with_solver("rev", "#b0101010101010101", "#b1010101010101010", 16); - check_unary_encoding_with_solver("rev", "#b1111111100000000", "#b0000000011111111", 16); - check_unary_encoding_with_solver("rev", "#b0000000000000000", "#b0000000000000000", 16); - check_unary_encoding_with_solver("rev", "#b1111111111111111", "#b1111111111111111", 16); - } - - #[test] - fn test_rev32_with_solver() { - check_unary_encoding_with_solver( - "rev", - "#b01010101010101010101010101010101", - "#b10101010101010101010101010101010", - 32, - ); - check_unary_encoding_with_solver( - "rev", - "#b11111111111111110000000000000000", - "#b00000000000000001111111111111111", - 32, - ); - check_unary_encoding_with_solver( - "rev", - "#b00000000000000000000000000000000", - "#b00000000000000000000000000000000", - 32, - ); - check_unary_encoding_with_solver( - "rev", - "#b11111111111111111111111111111111", - "#b11111111111111111111111111111111", - 32, - ); - } - - #[test] - fn test_rev64_with_solver() { - check_unary_encoding_with_solver( - "rev", - "#b0101010101010101010101010101010101010101010101010101010101010101", - "#b1010101010101010101010101010101010101010101010101010101010101010", - 64, - ); - check_unary_encoding_with_solver( - "rev", - "#b1111111111111111111111111111111100000000000000000000000000000000", - "#b0000000000000000000000000000000011111111111111111111111111111111", - 64, - ); - check_unary_encoding_with_solver( - "rev", - "#b0000000000000000000000000000000000000000000000000000000000000000", - "#b0000000000000000000000000000000000000000000000000000000000000000", - 64, - ); - check_unary_encoding_with_solver( - "rev", - "#b1111111111111111111111111111111111111111111111111111111111111111", - "#b1111111111111111111111111111111111111111111111111111111111111111", - 64, - ); - } - - #[test] - fn test_clz8_with_solver() { - check_unary_encoding_with_solver("clz", "#b00000000", "#b00001000", 8); - check_unary_encoding_with_solver("clz", "#b01111111", "#b00000001", 8); - check_unary_encoding_with_solver("clz", "#b11111111", "#b00000000", 8); - } - - #[test] - fn test_clz16_with_solver() { - check_unary_encoding_with_solver("clz", "#b0000000000000000", "#b0000000000010000", 16); - check_unary_encoding_with_solver("clz", "#b0000000000000001", "#b0000000000001111", 16); - check_unary_encoding_with_solver("clz", "#b0111111111111111", "#b0000000000000001", 16); - check_unary_encoding_with_solver("clz", "#b1111111111111111", "#b0000000000000000", 16); - } - - #[test] - fn test_clz32_with_solver() { - check_unary_encoding_with_solver( - "clz", - "#b00000000000000000000000000000000", - "#b00000000000000000000000000100000", - 32, - ); - check_unary_encoding_with_solver( - "clz", - "#b00000000000000000000000000000001", - "#b00000000000000000000000000011111", - 32, - ); - check_unary_encoding_with_solver( - "clz", - "#b01000000000000000000000000000000", - "#b00000000000000000000000000000001", - 32, - ); - check_unary_encoding_with_solver( - "clz", - "#b11111111111111111111111111111111", - "#b00000000000000000000000000000000", - 32, - ); - } - - #[test] - fn test_clz64_with_solver() { - check_unary_encoding_with_solver( - "clz", - "#b0000000000000000000000000000000000000000000000000000000000000000", - "#b0000000000000000000000000000000000000000000000000000000001000000", - 64, - ); - check_unary_encoding_with_solver( - "clz", - "#b0000000000000000000000000000000000000000000000000000000000000001", - "#b0000000000000000000000000000000000000000000000000000000000111111", - 64, - ); - check_unary_encoding_with_solver( - "clz", - "#b0100000000000000000000000000000000000000000000000000000000000000", - "#b0000000000000000000000000000000000000000000000000000000000000001", - 64, - ); - check_unary_encoding_with_solver( - "clz", - "#b1111111111111111111111111111111111111111111111111111111111111111", - "#b0000000000000000000000000000000000000000000000000000000000000000", - 64, - ); - } - - #[test] - fn test_cls8_with_solver() { - check_unary_encoding_with_solver("cls", "#b00000000", "#b00000111", 8); - check_unary_encoding_with_solver("cls", "#b01111111", "#b00000000", 8); - check_unary_encoding_with_solver("cls", "#b00111111", "#b00000001", 8); - check_unary_encoding_with_solver("cls", "#b11000000", "#b00000001", 8); - check_unary_encoding_with_solver("cls", "#b11111111", "#b00000111", 8); - } - - #[test] - fn test_cls16_with_solver() { - check_unary_encoding_with_solver("cls", "#b0000000000000000", "#b0000000000001111", 16); - check_unary_encoding_with_solver("cls", "#b0111111111111111", "#b0000000000000000", 16); - check_unary_encoding_with_solver("cls", "#b0011111111111111", "#b0000000000000001", 16); - check_unary_encoding_with_solver("cls", "#b1111111111111111", "#b0000000000001111", 16); - } - - #[test] - fn test_cls32_with_solver() { - check_unary_encoding_with_solver( - "cls", - "#b00000000000000000000000000000000", - "#b00000000000000000000000000011111", - 32, - ); - check_unary_encoding_with_solver( - "cls", - "#b01111111111111111111111111111111", - "#b00000000000000000000000000000000", - 32, - ); - check_unary_encoding_with_solver( - "cls", - "#b00100000000000000000000000000000", - "#b00000000000000000000000000000001", - 32, - ); - check_unary_encoding_with_solver( - "cls", - "#b11111111111111111111111111111111", - "#b00000000000000000000000000011111", - 32, - ); - } - - #[test] - fn test_cls64_with_solver() { - check_unary_encoding_with_solver( - "cls", - "#b0000000000000000000000000000000000000000000000000000000000000000", - "#b0000000000000000000000000000000000000000000000000000000000111111", - 64, - ); - check_unary_encoding_with_solver( - "cls", - "#b0010000000000000000000000000000000000000000000000000000000000000", - "#b0000000000000000000000000000000000000000000000000000000000000001", - 64, - ); - check_unary_encoding_with_solver( - "cls", - "#b0111111111111111111111111111111111111111111111111111111111111111", - "#b0000000000000000000000000000000000000000000000000000000000000000", - 64, - ); - check_unary_encoding_with_solver( - "cls", - "#b1111111111111111111111111111111111111111111111111111111111111111", - "#b0000000000000000000000000000000000000000000000000000000000111111", - 64, - ); - } - - #[test] - fn test_popcnt_8_with_solver() { - check_unary_encoding_with_solver("popcnt", "#b00000000", "#b00000000", 8); - check_unary_encoding_with_solver("popcnt", "#b11111111", "#b00001000", 8); - check_unary_encoding_with_solver("popcnt", "#b01010101", "#b00000100", 8); - } - - fn check_subs_with_solver(width: usize, x_str: &str, y_str: &str, output: &str) { - let mut ctx = get_ctx(); - - // Set up an input variable - let ty = ctx.smt.bit_vec_sort(ctx.smt.numeral(width)); - let x = ctx.declare("x".to_string(), ty); - let y = ctx.declare("y".to_string(), ty); - - // Set the input equal to our expected input - ctx.additional_assumptions - .push(ctx.smt.eq(x, ctx.smt.atom(x_str))); - ctx.additional_assumptions - .push(ctx.smt.eq(y, ctx.smt.atom(y_str))); - - // Call the encoding function to be tested - let call = subs::subs(&mut ctx, width, x, y, 0); - - // Output width always 68 bits - check_encoding_with_solver(ctx, call, output, 68) - } - - #[test] - fn test_subs_32_with_solver() { - check_subs_with_solver( - 32, - "#b00000000000000000000000000000000", - "#b00000000000000000000000000000000", - "#b01100000000000000000000000000000000000000000000000000000000000000000", - ); - - check_subs_with_solver( - 32, - "#b11111111111111111111111111111111", - "#b00000000000000000000000000000000", - "#b10100000000000000000000000000000000011111111111111111111111111111111", - ); - - check_subs_with_solver( - 32, - "#b10000000000010000000000000000000", - "#b00100111110000100011111110111000", - "#b00110000000000000000000000000000000001011000010001011100000001001000", - ); - } - - #[test] - fn test_subs_64_with_solver() { - check_subs_with_solver( - 64, - "#b0000000000000000000000000000000000000000000000000000000000000000", - "#b0000000000000000000000000000000000000000000000000000000000000000", - "#b01100000000000000000000000000000000000000000000000000000000000000000", - ); - } -} diff --git a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/popcnt.rs b/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/popcnt.rs deleted file mode 100644 index 283fc27ec337..000000000000 --- a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/popcnt.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::solver::SolverCtx; -use easy_smt::SExpr; - -// Future work: possibly move these into the annotation language or an SMTLIB prelude - -// Encoding strategy borrowed from -// https://github.com/fitzgen/synth-loop-free-prog/blob/6d04857693e4688eff4a36537840ba682353c2f3/src/component.rs#L219 -pub fn popcnt(s: &mut SolverCtx, ty: usize, x: SExpr, id: u32) -> SExpr { - let mut bits: Vec<_> = (0..ty) - .map(|i| s.zero_extend(7, s.smt.extract(i as i32, i as i32, x))) - .collect(); - let initial = bits.pop().unwrap(); - let r = bits.iter().fold(initial, |a, b| s.smt.bvadd(a, *b)); - - let id = format!("{ty}_{id}"); - let result = s.declare( - format!("popcnt_{id}"), - s.smt.list(vec![ - s.smt.atoms().und, - s.smt.atom("BitVec"), - s.smt.numeral(8), - ]), - ); - s.assume(s.smt.eq(result, r)); - result -} diff --git a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/rev.rs b/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/rev.rs deleted file mode 100644 index 09907feace02..000000000000 --- a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/rev.rs +++ /dev/null @@ -1,408 +0,0 @@ -use crate::solver::SolverCtx; -use easy_smt::SExpr; - -// Future work: possibly move these into the annotation language or an SMTLIB prelude - -pub fn rev64(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - // Generated code. - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - x1, - solver.smt.bvor( - solver.smt.bvlshr(x, solver.smt.atom("#x0000000000000020")), - solver.smt.bvshl(x, solver.smt.atom("#x0000000000000020")), - ), - )); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - x2, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x1, solver.smt.atom("#xffff0000ffff0000")), - solver.smt.atom("#x0000000000000010"), - ), - solver.smt.bvshl( - solver.smt.bvand(x1, solver.smt.atom("#x0000ffff0000ffff")), - solver.smt.atom("#x0000000000000010"), - ), - ), - )); - let x3 = solver.declare( - format!("x3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - x3, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x2, solver.smt.atom("#xff00ff00ff00ff00")), - solver.smt.atom("#x0000000000000008"), - ), - solver.smt.bvshl( - solver.smt.bvand(x2, solver.smt.atom("#x00ff00ff00ff00ff")), - solver.smt.atom("#x0000000000000008"), - ), - ), - )); - let x4 = solver.declare( - format!("x4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - x4, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x3, solver.smt.atom("#xf0f0f0f0f0f0f0f0")), - solver.smt.atom("#x0000000000000004"), - ), - solver.smt.bvshl( - solver.smt.bvand(x3, solver.smt.atom("#x0f0f0f0f0f0f0f0f")), - solver.smt.atom("#x0000000000000004"), - ), - ), - )); - let x5 = solver.declare( - format!("x5_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - x5, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x4, solver.smt.atom("#xcccccccccccccccc")), - solver.smt.atom("#x0000000000000002"), - ), - solver.smt.bvshl( - solver.smt.bvand(x4, solver.smt.atom("#x3333333333333333")), - solver.smt.atom("#x0000000000000002"), - ), - ), - )); - let rev64ret = solver.declare( - format!("rev64ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(64), - ]), - ); - solver.assume(solver.smt.eq( - rev64ret, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x5, solver.smt.atom("#xaaaaaaaaaaaaaaaa")), - solver.smt.atom("#x0000000000000001"), - ), - solver.smt.bvshl( - solver.smt.bvand(x5, solver.smt.atom("#x5555555555555555")), - solver.smt.atom("#x0000000000000001"), - ), - ), - )); - - rev64ret -} - -pub fn rev32(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(31, 0, x); - - // Generated code. - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - x1, - solver.smt.bvor( - solver.smt.bvlshr(x, solver.smt.atom("#x00000010")), - solver.smt.bvshl(x, solver.smt.atom("#x00000010")), - ), - )); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - x2, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x1, solver.smt.atom("#xff00ff00")), - solver.smt.atom("#x00000008"), - ), - solver.smt.bvshl( - solver.smt.bvand(x1, solver.smt.atom("#x00ff00ff")), - solver.smt.atom("#x00000008"), - ), - ), - )); - let x3 = solver.declare( - format!("x3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - x3, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x2, solver.smt.atom("#xf0f0f0f0")), - solver.smt.atom("#x00000004"), - ), - solver.smt.bvshl( - solver.smt.bvand(x2, solver.smt.atom("#x0f0f0f0f")), - solver.smt.atom("#x00000004"), - ), - ), - )); - let x4 = solver.declare( - format!("x4_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - x4, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x3, solver.smt.atom("#xcccccccc")), - solver.smt.atom("#x00000002"), - ), - solver.smt.bvshl( - solver.smt.bvand(x3, solver.smt.atom("#x33333333")), - solver.smt.atom("#x00000002"), - ), - ), - )); - let rev32ret = solver.declare( - format!("rev32ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(32), - ]), - ); - solver.assume(solver.smt.eq( - rev32ret, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x4, solver.smt.atom("#xaaaaaaaa")), - solver.smt.atom("#x00000001"), - ), - solver.smt.bvshl( - solver.smt.bvand(x4, solver.smt.atom("#x55555555")), - solver.smt.atom("#x00000001"), - ), - ), - )); - - rev32ret -} - -pub fn rev16(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(15, 0, x); - - // Generated code. - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.eq( - x1, - solver.smt.bvor( - solver.smt.bvlshr(x, solver.smt.atom("#x0008")), - solver.smt.bvshl(x, solver.smt.atom("#x0008")), - ), - )); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.eq( - x2, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x1, solver.smt.atom("#xf0f0")), - solver.smt.atom("#x0004"), - ), - solver.smt.bvshl( - solver.smt.bvand(x1, solver.smt.atom("#x0f0f")), - solver.smt.atom("#x0004"), - ), - ), - )); - let x3 = solver.declare( - format!("x3_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.eq( - x3, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x2, solver.smt.atom("#xcccc")), - solver.smt.atom("#x0002"), - ), - solver.smt.bvshl( - solver.smt.bvand(x2, solver.smt.atom("#x3333")), - solver.smt.atom("#x0002"), - ), - ), - )); - let rev16ret = solver.declare( - format!("rev16ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(16), - ]), - ); - solver.assume(solver.smt.eq( - rev16ret, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x3, solver.smt.atom("#xaaaa")), - solver.smt.atom("#x0001"), - ), - solver.smt.bvshl( - solver.smt.bvand(x3, solver.smt.atom("#x5555")), - solver.smt.atom("#x0001"), - ), - ), - )); - - let padding = solver.new_fresh_bits(solver.bitwidth - 16); - solver.smt.concat(padding, rev16ret) -} - -pub fn rev8(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(7, 0, x); - - // Generated code. - let x1 = solver.declare( - format!("x1_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.eq( - x1, - solver.smt.bvor( - solver.smt.bvlshr(x, solver.smt.atom("#x04")), - solver.smt.bvshl(x, solver.smt.atom("#x04")), - ), - )); - let x2 = solver.declare( - format!("x2_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.eq( - x2, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x1, solver.smt.atom("#xcc")), - solver.smt.atom("#x02"), - ), - solver.smt.bvshl( - solver.smt.bvand(x1, solver.smt.atom("#x33")), - solver.smt.atom("#x02"), - ), - ), - )); - let rev8ret = solver.declare( - format!("rev8ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(8), - ]), - ); - solver.assume(solver.smt.eq( - rev8ret, - solver.smt.bvor( - solver.smt.bvlshr( - solver.smt.bvand(x2, solver.smt.atom("#xaa")), - solver.smt.atom("#x01"), - ), - solver.smt.bvshl( - solver.smt.bvand(x2, solver.smt.atom("#x55")), - solver.smt.atom("#x01"), - ), - ), - )); - - let padding = solver.new_fresh_bits(solver.bitwidth - 8); - solver.smt.concat(padding, rev8ret) -} - -pub fn rev1(solver: &mut SolverCtx, x: SExpr, id: u32) -> SExpr { - let x = solver.smt.extract(0, 0, x); - - // Generated code. - let rev1ret = solver.declare( - format!("rev1ret_{id}"), - solver.smt.list(vec![ - solver.smt.atoms().und, - solver.smt.atom("BitVec"), - solver.smt.numeral(1), - ]), - ); - solver.assume(solver.smt.eq(rev1ret, x)); - - let padding = solver.new_fresh_bits(solver.bitwidth - 1); - solver.smt.concat(padding, rev1ret) -} diff --git a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/subs.rs b/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/subs.rs deleted file mode 100644 index 63b6537c64ea..000000000000 --- a/cranelift/isle/veri/veri_engine/src/solver/encoded_ops/subs.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::solver::SolverCtx; -use easy_smt::SExpr; - -// Future work: likely remove this when we add rule-chaining - -// Build the results of a subtraction with flags. Put the 4 flags in the high bits. -// Encoding adapted from SAIL ISLA: https://github.com/rems-project/isla -// -// N: Set to 1 when the result of the operation is negative -// Z: Set to 1 when the result of the operation is zero -// C: Set to 1 when the operation results in a carry, or when a subtraction results in no borrow -// V: Set to 1 when the operation causes overflow -// -// 67 66 65 64 63 ... 0 -// [ N | Z | C | V | ... result ... ] -pub fn subs(s: &mut SolverCtx, ty: usize, x: SExpr, y: SExpr, id: u32) -> SExpr { - let id = format!("{ty}_{id}"); - let (size, wide_size, x, y, zero, one, w_minus_one) = match ty { - 32 => ( - s.smt.numeral(32), - s.smt.numeral(32 * 2), - s.smt.extract(31, 0, x), - s.smt.extract(31, 0, y), - s.bv(0, 32), - s.bv(1, 32 * 2), - s.bv(31, 32), - ), - 64 => ( - s.smt.numeral(64), - s.smt.numeral(64 * 2), - s.smt.extract(63, 0, x), - s.smt.extract(63, 0, y), - s.bv(0, 64), - s.bv(1, 64 * 2), - s.bv(63, 64), - ), - _ => unreachable!(), - }; - - let b0 = s.bv(0, 1); - let b1 = s.bv(1, 1); - - // (define-const ynot (bvnot y)) - let ynot = s.declare( - format!("ynot_{id}"), - s.smt - .list(vec![s.smt.atoms().und, s.smt.atom("BitVec"), size]), - ); - s.assume(s.smt.eq(ynot, s.smt.bvnot(y))); - - // (define-const - // subs_wide - // (bvadd (bvadd ((_ zero_extend 64) x) ((_ zero_extend 64) ynot)) #x00000000000000000000000000000001)) - let subs_wide = s.declare( - format!("subs_wide_{id}"), - s.smt - .list(vec![s.smt.atoms().und, s.smt.atom("BitVec"), wide_size]), - ); - s.assume(s.smt.eq( - subs_wide, - s.smt.bvadd( - s.smt.bvadd(s.zero_extend(ty, x), s.zero_extend(ty, ynot)), - one, - ), - )); - - // (define-const subs ((_ extract 63 0) subs_wide)) - let subs = s.declare( - format!("subs_{id}"), - s.smt - .list(vec![s.smt.atoms().und, s.smt.atom("BitVec"), size]), - ); - s.assume(s.smt.eq( - subs, - s.smt.extract((ty - 1).try_into().unwrap(), 0, subs_wide), - )); - - // (define-const flags - // (concat (concat (concat - // ((_ extract 0 0) (bvlshr subs #x000000000000003f)) - // (ite (= subs #x0000000000000000) #b1 #b0)) - // (ite (= ((_ zero_extend 64) subs) subs_wide) #b0 #b1)) - // (ite (= ((_ sign_extend 64) subs) (bvadd (bvadd ((_ sign_extend 64) x) ((_ sign_extend 64) ynot)) #x00000000000000000000000000000001)) #b0 #b1))) - let flags = s.declare( - format!("flags_{id}"), - s.smt.list(vec![ - s.smt.atoms().und, - s.smt.atom("BitVec"), - s.smt.numeral(4), - ]), - ); - - // N: Set to 1 when the result of the operation is negative - // Z: Set to 1 when the result of the operation is zero - // C: Set to 1 when the operation results in a carry, or when a subtraction results in no borrow - // V: Set to 1 when the operation causes overflow - s.assume( - s.smt.eq( - flags, - s.smt.concat( - s.smt.concat( - s.smt.concat( - // N flag: result is negative - s.smt.extract(0, 0, s.smt.bvlshr(subs, w_minus_one)), - // Z flag: result is zero - s.smt.ite(s.smt.eq(subs, zero), b1, b0), - ), - // C flag: result has carry/subtraction has no borrow - s.smt - .ite(s.smt.eq(s.zero_extend(ty, subs), subs_wide), b0, b1), - ), - // V: operation causes overflow - s.smt.ite( - s.smt.eq( - s.sign_extend(ty, subs), - s.smt.bvadd( - s.smt.bvadd(s.sign_extend(ty, x), s.sign_extend(ty, ynot)), - one, - ), - ), - b0, - b1, - ), - ), - ), - ); - - let ret = s.declare( - format!("subs_ret_{id}"), - s.smt.list(vec![ - s.smt.atoms().und, - s.smt.atom("BitVec"), - s.smt.numeral(68), - ]), - ); - - s.assume(s.smt.eq( - ret, - match ty { - // Pad 32 back to full reg width of 64 before adding flags to the left - 32 => s.smt.concat(flags, s.zero_extend(ty, subs)), - 64 => s.smt.concat(flags, subs), - _ => unreachable!(), - }, - )); - ret -} diff --git a/cranelift/isle/veri/veri_engine/src/termname.rs b/cranelift/isle/veri/veri_engine/src/termname.rs deleted file mode 100644 index 20b1035237d7..000000000000 --- a/cranelift/isle/veri/veri_engine/src/termname.rs +++ /dev/null @@ -1,31 +0,0 @@ -use cranelift_isle as isle; -use isle::sema::{Pattern, TermEnv, TypeEnv}; - -/// Check whether the pattern (the LHS term) contains a given term name, -/// including in any subterms. -pub fn pattern_contains_termname( - pat: &Pattern, - name: &str, - termenv: &TermEnv, - typeenv: &TypeEnv, -) -> bool { - match pat { - Pattern::BindPattern(..) - | Pattern::Var(..) - | Pattern::ConstInt(..) - | Pattern::ConstBool(..) - | Pattern::ConstPrim(..) - | Pattern::Wildcard(..) => false, - Pattern::Term(_, termid, arg_patterns) => { - let term = &termenv.terms[termid.index()]; - let term_name = &typeenv.syms[term.name.index()]; - (term_name == name) - || arg_patterns - .iter() - .any(|p| pattern_contains_termname(p, name, termenv, typeenv)) - } - Pattern::And(_, children) => children - .iter() - .any(|p| pattern_contains_termname(p, name, termenv, typeenv)), - } -} diff --git a/cranelift/isle/veri/veri_engine/src/type_inference.rs b/cranelift/isle/veri/veri_engine/src/type_inference.rs deleted file mode 100644 index 29621abba189..000000000000 --- a/cranelift/isle/veri/veri_engine/src/type_inference.rs +++ /dev/null @@ -1,2452 +0,0 @@ -use itertools::Itertools; -use std::collections::{HashMap, HashSet}; -use std::hash::Hash; - -use crate::annotations::AnnotationEnv; -use crate::termname::pattern_contains_termname; -use cranelift_isle as isle; -use isle::sema::{Pattern, TermEnv, TermId, TypeEnv, VarId}; -use itertools::izip; -use veri_ir::{annotation_ir, ConcreteTest, Expr, TermSignature, Type, TypeContext}; - -use crate::{Config, FLAGS_WIDTH, REG_WIDTH}; - -#[derive(Clone, Debug)] -struct RuleParseTree { - // a map of var name to type variable, where var could be - // Pattern::Var or var used in Pattern::BindPattern - varid_to_type_var_map: HashMap, - // a map of type var to value, if known - type_var_to_val_map: HashMap, - // bookkeeping that tells the next unused type var - next_type_var: u32, - // combined constraints from all nodes - concrete_constraints: HashSet, - var_constraints: HashSet, - bv_constraints: HashSet, - - ty_vars: HashMap, - quantified_vars: HashSet<(String, u32)>, - free_vars: HashSet<(String, u32)>, - // Used to check distinct models - term_input_bvs: Vec, - // Used for custom verification conditions - term_args: Vec, - lhs_assumptions: Vec, - rhs_assumptions: Vec, - - rhs_assertions: Vec, - concrete: Option, -} - -#[derive(Clone, Debug)] -pub enum TypeVarConstruct { - Var, - BindPattern, - Wildcard(u32), - Term(TermId), - Bool(bool), - Const(i128), - Let(Vec), - And, -} - -#[derive(Clone, Debug)] -pub struct TypeVarNode { - ident: String, - construct: TypeVarConstruct, - type_var: u32, - children: Vec, - assertions: Vec, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -// Constraints either assign concrete types to type variables -// or set them equal to other type variables -enum TypeExpr { - Concrete(u32, annotation_ir::Type), - Variable(u32, u32), - // The type variable of the first arg is equal to the value of the second - WidthInt(u32, u32), -} - -#[derive(Debug)] -pub struct AnnotationTypeInfo { - // map of annotation variable to assigned type var - pub term: String, - pub var_to_type_var: HashMap, -} - -#[derive(Debug)] -pub struct RuleSemantics { - pub annotation_infos: Vec, - - // map of type var to solved type - pub type_var_to_type: HashMap, - - pub lhs: veri_ir::Expr, - pub rhs: veri_ir::Expr, - pub quantified_vars: Vec, - pub free_vars: Vec, - pub term_input_bvs: Vec, - // Used for custom verification conditions - pub term_args: Vec, - pub lhs_assumptions: Vec, - pub rhs_assumptions: Vec, - pub rhs_assertions: Vec, - pub tyctx: TypeContext, -} - -pub fn type_rules_with_term_and_types( - termenv: &TermEnv, - typeenv: &TypeEnv, - annotation_env: &AnnotationEnv, - config: &Config, - types: &TermSignature, - concrete: &Option, -) -> HashMap { - let mut solutions = HashMap::new(); - - for rule in &termenv.rules { - // Only type rules with the given term on the LHS - if !pattern_contains_termname( - // Hack for now: typeid not used - &Pattern::Term( - cranelift_isle::sema::TypeId(0), - rule.root_term, - rule.args.clone(), - ), - &config.term, - termenv, - typeenv, - ) { - continue; - } - if let Some(names) = &config.names { - if rule.name.is_none() { - continue; - } - let name = &typeenv.syms[rule.name.unwrap().index()]; - if !names.contains(name) { - continue; - } - } - if let Some(s) = type_annotations_using_rule( - rule, - annotation_env, - typeenv, - termenv, - &config.term, - types, - concrete, - ) { - solutions.insert(rule.id, s); - } - } - solutions -} - -fn convert_type(aty: &annotation_ir::Type) -> veri_ir::Type { - match aty { - annotation_ir::Type::BitVectorUnknown(..) => veri_ir::Type::BitVector(None), - annotation_ir::Type::BitVector => veri_ir::Type::BitVector(None), - annotation_ir::Type::BitVectorWithWidth(w) => veri_ir::Type::BitVector(Some(*w)), - annotation_ir::Type::Int => veri_ir::Type::Int, - annotation_ir::Type::Bool => veri_ir::Type::Bool, - annotation_ir::Type::Unit => veri_ir::Type::Unit, - annotation_ir::Type::Poly(_) => veri_ir::Type::BitVector(None), - } -} - -fn type_annotations_using_rule<'a>( - rule: &'a isle::sema::Rule, - annotation_env: &'a AnnotationEnv, - typeenv: &'a TypeEnv, - termenv: &'a TermEnv, - term: &String, - types: &TermSignature, - concrete: &'a Option, -) -> Option { - let mut parse_tree = RuleParseTree { - varid_to_type_var_map: HashMap::new(), - type_var_to_val_map: HashMap::new(), - next_type_var: 1, - concrete_constraints: HashSet::new(), - var_constraints: HashSet::new(), - bv_constraints: HashSet::new(), - ty_vars: HashMap::new(), - quantified_vars: HashSet::new(), - free_vars: HashSet::new(), - term_input_bvs: vec![], - term_args: vec![], - lhs_assumptions: vec![], - rhs_assumptions: vec![], - rhs_assertions: vec![], - concrete: concrete.clone(), - }; - - let mut annotation_infos = vec![]; - if !rule.iflets.is_empty() { - for iflet in &rule.iflets { - let iflet_lhs = &mut create_parse_tree_pattern( - rule, - &iflet.lhs, - &mut parse_tree, - typeenv, - termenv, - term, - types, - ); - let iflet_rhs = - &mut create_parse_tree_expr(rule, &iflet.rhs, &mut parse_tree, typeenv, termenv); - - let iflet_lhs_expr = add_rule_constraints( - &mut parse_tree, - iflet_lhs, - termenv, - typeenv, - annotation_env, - &mut annotation_infos, - false, - ); - iflet_lhs_expr.as_ref()?; - - let iflet_rhs_expr = add_rule_constraints( - &mut parse_tree, - iflet_rhs, - termenv, - typeenv, - annotation_env, - &mut annotation_infos, - false, - ); - iflet_rhs_expr.as_ref()?; - parse_tree - .var_constraints - .insert(TypeExpr::Variable(iflet_lhs.type_var, iflet_rhs.type_var)); - // Add if-lets to the LHS - parse_tree.lhs_assumptions.push(veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(iflet_lhs_expr.unwrap()), - Box::new(iflet_rhs_expr.unwrap()), - )); - } - } - let lhs = &mut create_parse_tree_pattern( - rule, - // Hack for now: typeid not used - &isle::sema::Pattern::Term( - cranelift_isle::sema::TypeId(0), - rule.root_term, - rule.args.clone(), - ), - &mut parse_tree, - typeenv, - termenv, - term, - types, - ); - let rhs = &mut create_parse_tree_expr(rule, &rule.rhs, &mut parse_tree, typeenv, termenv); - - log::trace!("LHS:"); - let lhs_expr = add_rule_constraints( - &mut parse_tree, - lhs, - termenv, - typeenv, - annotation_env, - &mut annotation_infos, - false, - ); - lhs_expr.as_ref()?; - log::trace!("\n\tRHS:"); - let rhs_expr = add_rule_constraints( - &mut parse_tree, - rhs, - termenv, - typeenv, - annotation_env, - &mut annotation_infos, - true, - ); - rhs_expr.as_ref()?; - - match (lhs_expr, rhs_expr) { - (Some(lhs_expr), Some(rhs_expr)) => { - parse_tree - .var_constraints - .insert(TypeExpr::Variable(lhs.type_var, rhs.type_var)); - - let (solution, bv_unknown_width_sets) = solve_constraints( - parse_tree.concrete_constraints, - parse_tree.var_constraints, - parse_tree.bv_constraints, - &mut parse_tree.type_var_to_val_map, - Some(&parse_tree.ty_vars), - ); - - let mut tymap = HashMap::new(); - - for (expr, t) in &parse_tree.ty_vars { - if let Some(ty) = solution.get(t) { - tymap.insert(*t, convert_type(ty)); - } else { - panic!("missing type variable {t} in solution for: {expr:?}"); - } - } - let mut quantified_vars = vec![]; - for (s, t) in parse_tree.quantified_vars.iter().sorted() { - let expr = veri_ir::Expr::Terminal(veri_ir::Terminal::Var(s.clone())); - if let Some(ty) = solution.get(t) { - let ty = convert_type(ty); - parse_tree.ty_vars.insert(expr, *t); - tymap.insert(*t, ty); - quantified_vars.push(veri_ir::BoundVar { - name: s.clone(), - tyvar: *t, - }); - } else { - panic!("missing type variable {t} in solution for: {expr:?}"); - } - } - let mut free_vars = vec![]; - for (s, t) in parse_tree.free_vars { - let expr = veri_ir::Expr::Terminal(veri_ir::Terminal::Var(s.clone())); - if let Some(ty) = solution.get(&t) { - let ty = convert_type(ty); - parse_tree.ty_vars.insert(expr, t); - tymap.insert(t, ty); - free_vars.push(veri_ir::BoundVar { name: s, tyvar: t }); - } else { - panic!("missing type variable {t} in solution for: {expr:?}"); - } - } - - Some(RuleSemantics { - annotation_infos, - type_var_to_type: solution, - lhs: lhs_expr, - rhs: rhs_expr, - lhs_assumptions: parse_tree.lhs_assumptions, - rhs_assumptions: parse_tree.rhs_assumptions, - rhs_assertions: parse_tree.rhs_assertions, - quantified_vars, - free_vars, - term_input_bvs: parse_tree.term_input_bvs, - term_args: parse_tree.term_args, - tyctx: TypeContext { - tyvars: parse_tree.ty_vars.clone(), - tymap, - tyvals: parse_tree.type_var_to_val_map, - bv_unknown_width_sets, - }, - }) - } - _ => None, - } -} - -fn const_fold_to_int(e: &veri_ir::Expr) -> Option { - match e { - Expr::Terminal(veri_ir::Terminal::Const(c, _)) => Some(*c), - _ => None, - } -} - -fn add_annotation_constraints( - expr: annotation_ir::Expr, - tree: &mut RuleParseTree, - annotation_info: &mut AnnotationTypeInfo, -) -> (veri_ir::Expr, u32) { - let (e, t) = match expr { - annotation_ir::Expr::Var(x, ..) => { - if !annotation_info.var_to_type_var.contains_key(&x) { - panic!("Error: unbound variable: {x}"); - } - let t = annotation_info.var_to_type_var[&x]; - let name = format!("{}__{}__{}", annotation_info.term, x, t); - (veri_ir::Expr::Terminal(veri_ir::Terminal::Var(name)), t) - } - annotation_ir::Expr::Const(c, ..) => { - let t = tree.next_type_var; - let e = veri_ir::Expr::Terminal(veri_ir::Terminal::Const(c.value, t)); - match c.ty { - annotation_ir::Type::BitVector => { - let ty = annotation_ir::Type::BitVectorWithWidth(c.width); - tree.concrete_constraints.insert(TypeExpr::Concrete(t, ty)); - } - _ => { - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, c.ty.clone())); - } - } - tree.next_type_var += 1; - - // If constant is known, add the value to the tree. Useful for - // capturing isleTypes - tree.type_var_to_val_map.insert(t, c.value); - (e, t) - } - annotation_ir::Expr::True => { - let t = tree.next_type_var; - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - - tree.next_type_var += 1; - (veri_ir::Expr::Terminal(veri_ir::Terminal::True), t) - } - annotation_ir::Expr::False => { - let t = tree.next_type_var; - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - - tree.next_type_var += 1; - (veri_ir::Expr::Terminal(veri_ir::Terminal::False), t) - } - - annotation_ir::Expr::WidthOf(x) => { - let (ex, tx) = add_annotation_constraints(*x.clone(), tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - tree.bv_constraints - .insert(TypeExpr::Concrete(tx, annotation_ir::Type::BitVector)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Int)); - tree.concrete_constraints.insert(TypeExpr::WidthInt(tx, t)); - (veri_ir::Expr::WidthOf(Box::new(ex)), t) - } - - annotation_ir::Expr::Eq(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::Eq, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::Imp(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::Bool)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::Bool)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::Imp, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::Lte(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::Lte, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::Not(x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::Bool)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - - tree.next_type_var += 1; - (veri_ir::Expr::Unary(veri_ir::UnaryOp::Not, Box::new(e1)), t) - } - annotation_ir::Expr::Or(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::Bool)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::Bool)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::Or, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::And(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::Bool)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::Bool)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::And, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVSgt(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSgt, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVSgte(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSgte, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVSlt(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSlt, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVSlte(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSlte, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVUgt(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVUgt, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVUgte(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVUgte, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVUlt(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVUlt, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVUlte(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVUlte, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVSaddo(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSaddo, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVNeg(x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - - let t = tree.next_type_var; - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Unary(veri_ir::UnaryOp::BVNeg, Box::new(e1)), - t, - ) - } - annotation_ir::Expr::BVNot(x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - - let t = tree.next_type_var; - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Unary(veri_ir::UnaryOp::BVNot, Box::new(e1)), - t, - ) - } - - annotation_ir::Expr::BVMul(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVMul, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVUDiv(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVUDiv, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVSDiv(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSDiv, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVAdd(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVAdd, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVSub(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSub, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVUrem(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVUrem, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVSrem(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVSrem, Box::new(e1), Box::new(e2)), - t, - ) - } - - annotation_ir::Expr::BVAnd(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVAnd, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVOr(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVOr, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVXor(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVXor, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVRotl(x, a) => { - let (xe, xt) = add_annotation_constraints(*x, tree, annotation_info); - let (ae, at) = add_annotation_constraints(*a, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - tree.bv_constraints - .insert(TypeExpr::Concrete(xt, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(at, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, xt)); - - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVRotl, Box::new(xe), Box::new(ae)), - t, - ) - } - annotation_ir::Expr::BVRotr(x, a) => { - let (xe, xt) = add_annotation_constraints(*x, tree, annotation_info); - let (ae, at) = add_annotation_constraints(*a, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - tree.bv_constraints - .insert(TypeExpr::Concrete(xt, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(at, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, xt)); - - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVRotr, Box::new(xe), Box::new(ae)), - t, - ) - } - annotation_ir::Expr::BVShl(x, a) => { - let (xe, xt) = add_annotation_constraints(*x, tree, annotation_info); - let (ae, at) = add_annotation_constraints(*a, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - tree.bv_constraints - .insert(TypeExpr::Concrete(xt, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(at, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, xt)); - tree.var_constraints.insert(TypeExpr::Variable(xt, at)); - - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVShl, Box::new(xe), Box::new(ae)), - t, - ) - } - annotation_ir::Expr::BVShr(x, a) => { - let (xe, xt) = add_annotation_constraints(*x, tree, annotation_info); - let (ae, at) = add_annotation_constraints(*a, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - tree.bv_constraints - .insert(TypeExpr::Concrete(xt, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(at, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, xt)); - tree.var_constraints.insert(TypeExpr::Variable(xt, at)); - - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVShr, Box::new(xe), Box::new(ae)), - t, - ) - } - annotation_ir::Expr::BVAShr(x, a) => { - let (xe, xt) = add_annotation_constraints(*x, tree, annotation_info); - let (ae, at) = add_annotation_constraints(*a, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - tree.bv_constraints - .insert(TypeExpr::Concrete(xt, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(at, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, xt)); - tree.var_constraints.insert(TypeExpr::Variable(at, xt)); - - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::BVAShr, Box::new(xe), Box::new(ae)), - t, - ) - } - annotation_ir::Expr::Lt(x, y) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Binary(veri_ir::BinaryOp::Lt, Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVConvTo(w, x) => { - let (we, wt) = add_annotation_constraints(*w, tree, annotation_info); - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - // In the dynamic case, we don't know the width at this point - tree.concrete_constraints - .insert(TypeExpr::Concrete(wt, annotation_ir::Type::Int)); - - if let Some(w) = const_fold_to_int(&we) { - tree.concrete_constraints.insert(TypeExpr::Concrete( - t, - annotation_ir::Type::BitVectorWithWidth(w.try_into().unwrap()), - )); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - let t2 = tree.next_type_var; - tree.next_type_var += 1; - let width = Expr::Terminal(veri_ir::Terminal::Const(w, t2)); - tree.type_var_to_val_map.insert(t2, w); - tree.ty_vars.insert(width.clone(), t2); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::Int)); - (veri_ir::Expr::BVConvTo(Box::new(width), Box::new(e1)), t) - } else { - tree.concrete_constraints.insert(TypeExpr::WidthInt(t, wt)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - - (veri_ir::Expr::BVConvTo(Box::new(we), Box::new(e1)), t) - } - } - annotation_ir::Expr::BVSignExtToVarWidth(w, x) => { - let (we, wt) = add_annotation_constraints(*w, tree, annotation_info); - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - // In the dynamic case, we don't know the width at this point - tree.concrete_constraints - .insert(TypeExpr::Concrete(wt, annotation_ir::Type::Int)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - - ( - veri_ir::Expr::BVSignExtToVarWidth(Box::new(we), Box::new(e1)), - t, - ) - } - annotation_ir::Expr::BVZeroExtTo(w, x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - let width = match *w { - veri_ir::annotation_ir::Width::Const(c) => c, - veri_ir::annotation_ir::Width::RegWidth => REG_WIDTH, - }; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.concrete_constraints.insert(TypeExpr::Concrete( - t, - annotation_ir::Type::BitVectorWithWidth(width), - )); - - (veri_ir::Expr::BVZeroExtTo(width, Box::new(e1)), t) - } - annotation_ir::Expr::BVZeroExtToVarWidth(w, x) => { - let (we, wt) = add_annotation_constraints(*w, tree, annotation_info); - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let t = tree.next_type_var; - tree.next_type_var += 1; - - // In the dynamic case, we don't know the width at this point - tree.concrete_constraints - .insert(TypeExpr::Concrete(wt, annotation_ir::Type::Int)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - - ( - veri_ir::Expr::BVZeroExtToVarWidth(Box::new(we), Box::new(e1)), - t, - ) - } - annotation_ir::Expr::BVSignExtTo(w, x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let t = tree.next_type_var; - - let width = match *w { - veri_ir::annotation_ir::Width::Const(c) => c, - veri_ir::annotation_ir::Width::RegWidth => REG_WIDTH, - }; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.concrete_constraints.insert(TypeExpr::Concrete( - t, - annotation_ir::Type::BitVectorWithWidth(width), - )); - - tree.next_type_var += 1; - - (veri_ir::Expr::BVSignExtTo(width, Box::new(e1)), t) - } - annotation_ir::Expr::BVExtract(l, r, x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.concrete_constraints.insert(TypeExpr::Concrete( - t, - annotation_ir::Type::BitVectorWithWidth(l - r + 1), - )); - - tree.next_type_var += 1; - - (veri_ir::Expr::BVExtract(l, r, Box::new(e1)), t) - } - annotation_ir::Expr::BVConcat(xs) => { - // AVH todo: doesn't sum the various widths, has to be done in the solver - let t = tree.next_type_var; - tree.next_type_var += 1; - - let mut exprs = vec![]; - for x in xs { - let (xe, xt) = add_annotation_constraints(x, tree, annotation_info); - tree.bv_constraints - .insert(TypeExpr::Concrete(xt, annotation_ir::Type::BitVector)); - exprs.push(xe); - } - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - - tree.next_type_var += 1; - - (veri_ir::Expr::BVConcat(exprs), t) - } - annotation_ir::Expr::BVIntToBv(w, x) => { - let (ex, tx) = add_annotation_constraints(*x.clone(), tree, annotation_info); - - let t = tree.next_type_var; - tree.next_type_var += 1; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(tx, annotation_ir::Type::Int)); - - tree.concrete_constraints.insert(TypeExpr::Concrete( - t, - annotation_ir::Type::BitVectorWithWidth(w), - )); - - (veri_ir::Expr::BVIntToBV(w, Box::new(ex)), t) - } - annotation_ir::Expr::BVToInt(x) => { - let (ex, tx) = add_annotation_constraints(*x.clone(), tree, annotation_info); - - let t = tree.next_type_var; - tree.next_type_var += 1; - - tree.bv_constraints - .insert(TypeExpr::Concrete(tx, annotation_ir::Type::BitVector)); - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Int)); - - (veri_ir::Expr::BVToInt(Box::new(ex)), t) - } - annotation_ir::Expr::Conditional(c, t, e) => { - let (e1, t1) = add_annotation_constraints(*c, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*t, tree, annotation_info); - let (e3, t3) = add_annotation_constraints(*e, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::Bool)); - tree.var_constraints.insert(TypeExpr::Variable(t2, t3)); - tree.var_constraints.insert(TypeExpr::Variable(t, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::Conditional(Box::new(e1), Box::new(e2), Box::new(e3)), - t, - ) - } - annotation_ir::Expr::Switch(c, cases) => { - let (c_expr, c_t) = add_annotation_constraints(*c, tree, annotation_info); - - let t = tree.next_type_var; - tree.next_type_var += 1; - - let mut case_exprs = vec![]; - for (m, b) in cases { - let (case_expr, case_t) = - add_annotation_constraints(m.clone(), tree, annotation_info); - let (body_expr, body_t) = - add_annotation_constraints(b.clone(), tree, annotation_info); - - tree.var_constraints.insert(TypeExpr::Variable(c_t, case_t)); - tree.var_constraints.insert(TypeExpr::Variable(t, body_t)); - case_exprs.push((case_expr, body_expr)); - } - (veri_ir::Expr::Switch(Box::new(c_expr), case_exprs), t) - } - annotation_ir::Expr::CLZ(x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - - let t = tree.next_type_var; - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - - tree.next_type_var += 1; - (veri_ir::Expr::CLZ(Box::new(e1)), t) - } - annotation_ir::Expr::CLS(x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - - let t = tree.next_type_var; - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - - tree.next_type_var += 1; - (veri_ir::Expr::CLS(Box::new(e1)), t) - } - annotation_ir::Expr::Rev(x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - - let t = tree.next_type_var; - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - - tree.next_type_var += 1; - (veri_ir::Expr::Rev(Box::new(e1)), t) - } - annotation_ir::Expr::BVSubs(ty, x, y) => { - let (e0, t0) = add_annotation_constraints(*ty, tree, annotation_info); - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - - let t = tree.next_type_var; - - // For aarch64, subs sets 4 flags. Model these as 4 bit appended to the left of the - // register. - tree.concrete_constraints.insert(TypeExpr::Concrete( - t, - annotation_ir::Type::BitVectorWithWidth(REG_WIDTH + FLAGS_WIDTH), - )); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t0, annotation_ir::Type::Int)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t1, t2)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::BVSubs(Box::new(e0), Box::new(e1), Box::new(e2)), - t, - ) - } - annotation_ir::Expr::BVPopcnt(x) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.var_constraints.insert(TypeExpr::Variable(t, t1)); - - tree.next_type_var += 1; - (veri_ir::Expr::BVPopcnt(Box::new(e1)), t) - } - annotation_ir::Expr::LoadEffect(x, y, z) => { - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let (e3, t3) = add_annotation_constraints(*z, tree, annotation_info); - let t = tree.next_type_var; - - tree.bv_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::BitVector)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::Int)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t3, annotation_ir::Type::BitVector)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::LoadEffect(Box::new(e1), Box::new(e2), Box::new(e3)), - t, - ) - } - annotation_ir::Expr::StoreEffect(w, x, y, z) => { - let (e0, t0) = add_annotation_constraints(*w, tree, annotation_info); - let (e1, t1) = add_annotation_constraints(*x, tree, annotation_info); - let (e2, t2) = add_annotation_constraints(*y, tree, annotation_info); - let (e3, t3) = add_annotation_constraints(*z, tree, annotation_info); - let t = tree.next_type_var; - - tree.concrete_constraints - .insert(TypeExpr::Concrete(t, annotation_ir::Type::Unit)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t0, annotation_ir::Type::BitVector)); - tree.concrete_constraints - .insert(TypeExpr::Concrete(t1, annotation_ir::Type::Int)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t2, annotation_ir::Type::BitVector)); - tree.bv_constraints - .insert(TypeExpr::Concrete(t3, annotation_ir::Type::BitVector)); - - tree.next_type_var += 1; - ( - veri_ir::Expr::StoreEffect(Box::new(e0), Box::new(e1), Box::new(e2), Box::new(e3)), - t, - ) - } - }; - tree.ty_vars.insert(e.clone(), t); - // let fmt = format!("{}:\t{:?}", t, e); - // dbg!(fmt); - (e, t) -} - -fn add_isle_constraints( - term: &isle::sema::Term, - tree: &mut RuleParseTree, - annotation_env: &AnnotationEnv, - annotation_info: &mut AnnotationTypeInfo, - annotation: annotation_ir::TermSignature, -) { - let mut annotation_vars = vec![]; - for a in annotation.args { - annotation_vars.push(a.name); - } - annotation_vars.push(annotation.ret.name); - - let mut isle_types = vec![]; - for arg_ty in term.arg_tys.iter() { - isle_types.push(*arg_ty); - } - isle_types.push(term.ret_ty); - assert_eq!(annotation_vars.len(), isle_types.len()); - - for (isle_type_id, annotation_var) in isle_types.iter().zip(annotation_vars) { - // in case the var was not in the annotation - if !annotation_info - .var_to_type_var - .contains_key(&annotation_var) - { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - - annotation_info - .var_to_type_var - .insert(annotation_var.clone(), type_var); - } - - if let Some(ir_type) = annotation_env.model_map.get(isle_type_id) { - let type_var = annotation_info.var_to_type_var[&annotation_var]; - match ir_type { - annotation_ir::Type::BitVector => tree - .bv_constraints - .insert(TypeExpr::Concrete(type_var, ir_type.clone())), - _ => tree - .concrete_constraints - .insert(TypeExpr::Concrete(type_var, ir_type.clone())), - }; - } - } -} - -fn add_rule_constraints( - tree: &mut RuleParseTree, - curr: &mut TypeVarNode, - termenv: &TermEnv, - typeenv: &TypeEnv, - annotation_env: &AnnotationEnv, - annotation_infos: &mut Vec, - rhs: bool, -) -> Option { - // Only relate args to annotations for terms. For leaves, return immediately. - // For recursive definitions without annotations (like And and Let), recur. - let mut children = vec![]; - for child in &mut curr.children { - if let Some(e) = add_rule_constraints( - tree, - child, - termenv, - typeenv, - annotation_env, - annotation_infos, - rhs, - ) { - children.push(e); - } else { - return None; - } - } - let e = match &curr.construct { - TypeVarConstruct::Var => { - tree.quantified_vars - .insert((curr.ident.clone(), curr.type_var)); - tree.free_vars.insert((curr.ident.clone(), curr.type_var)); - Some(veri_ir::Expr::Terminal(veri_ir::Terminal::Var( - curr.ident.clone(), - ))) - } - TypeVarConstruct::BindPattern => { - assert_eq!(children.len(), 2); - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(children[0].clone()), - Box::new(children[1].clone()), - ); - if rhs { - tree.rhs_assumptions.push(eq); - } else { - tree.lhs_assumptions.push(eq); - } - Some(children[0].clone()) - } - TypeVarConstruct::Wildcard(i) => { - Some(veri_ir::Expr::Terminal(veri_ir::Terminal::Wildcard(*i))) - } - TypeVarConstruct::Const(i) => { - // If constant is known, add the value to the tree. Useful for - // capturing isleTypes - tree.type_var_to_val_map.insert(curr.type_var, *i); - - Some(veri_ir::Expr::Terminal(veri_ir::Terminal::Const( - *i, - curr.type_var, - ))) - } - TypeVarConstruct::Bool(val) => { - // If constant is known, add the value to the tree. Useful for - // capturing isleTypes - tree.type_var_to_val_map - .insert(curr.type_var, i128::from(*val)); - - Some(veri_ir::Expr::Terminal(veri_ir::Terminal::Const( - i128::from(*val), - curr.type_var, - ))) - } - TypeVarConstruct::And => { - tree.quantified_vars - .insert((curr.ident.clone(), curr.type_var)); - let first = &children[0]; - for (i, e) in children.iter().enumerate() { - if i != 0 { - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(first.clone()), - Box::new(e.clone()), - ); - if rhs { - tree.rhs_assumptions.push(eq); - } else { - tree.lhs_assumptions.push(eq); - } - } - } - Some(first.to_owned()) - } - TypeVarConstruct::Let(bound) => { - tree.quantified_vars - .insert((curr.ident.clone(), curr.type_var)); - for (e, s) in children.iter().zip(bound) { - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(veri_ir::Expr::Terminal(veri_ir::Terminal::Var( - s.to_owned(), - ))), - Box::new(e.to_owned()), - ); - if rhs { - tree.rhs_assumptions.push(eq); - } else { - tree.lhs_assumptions.push(eq); - } - } - children.last().cloned() - } - TypeVarConstruct::Term(term_id) => { - let term = &termenv.terms[term_id.index()]; - let term_name = typeenv.syms[term.name.index()].clone(); - - // Print term for debugging - log::trace!(" {term_name}"); - - tree.quantified_vars - .insert((curr.ident.clone(), curr.type_var)); - let a = annotation_env.get_annotation_for_term(term_id); - if a.is_none() { - log::error!("\nSkipping rule with unannotated term: {term_name}"); - return None; - } - let annotation = a.unwrap(); - - // Test code only: support providing concrete inputs - if let Some(concrete) = &tree.concrete { - if concrete.termname == term_name { - for (child, node, input) in - izip!(&children, curr.children.iter(), &concrete.args) - { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - let lit = veri_ir::Expr::Terminal(veri_ir::Terminal::Literal( - input.literal.clone(), - type_var, - )); - tree.var_constraints - .insert(TypeExpr::Variable(node.type_var, type_var)); - tree.ty_vars.insert(lit.clone(), type_var); - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(child.clone()), - Box::new(lit), - ); - curr.assertions.push(eq.clone()); - if rhs { - tree.rhs_assumptions.push(eq); - } else { - tree.lhs_assumptions.push(eq); - } - } - } - } - - // use a fresh mapping for each term - // keep the same mapping between assertions in the same annotation - let mut annotation_info = AnnotationTypeInfo { - term: curr.ident.clone(), - var_to_type_var: HashMap::new(), - }; - for arg in &annotation.sig.args { - annotation_info - .var_to_type_var - .insert(arg.name.clone(), tree.next_type_var); - tree.next_type_var += 1; - } - annotation_info - .var_to_type_var - .insert(annotation.sig.ret.name.clone(), tree.next_type_var); - tree.next_type_var += 1; - - for expr in annotation.assumptions { - let (typed_expr, _) = add_annotation_constraints(*expr, tree, &mut annotation_info); - curr.assertions.push(typed_expr.clone()); - if rhs { - tree.rhs_assumptions.push(typed_expr); - } else { - tree.lhs_assumptions.push(typed_expr); - } - add_isle_constraints( - term, - tree, - annotation_env, - &mut annotation_info, - annotation.sig.clone(), - ); - } - // For assertions, global assume if not RHS, otherwise assert - for expr in annotation.assertions { - let (typed_expr, _) = add_annotation_constraints(*expr, tree, &mut annotation_info); - curr.assertions.push(typed_expr.clone()); - add_isle_constraints( - term, - tree, - annotation_env, - &mut annotation_info, - annotation.sig.clone(), - ); - if rhs { - tree.rhs_assertions.push(typed_expr); - } else { - tree.lhs_assumptions.push(typed_expr); - } - } - - // set args in rule equal to args in annotation - for (child, arg) in curr.children.iter().zip(&annotation.sig.args) { - let rule_type_var = child.type_var; - if !annotation_info.var_to_type_var.contains_key(&arg.name) { - continue; - } - let annotation_type_var = annotation_info.var_to_type_var[&arg.name]; - - // essentially constant propagate: if we know the value from the rule arg being - // provided as a literal, propagate this to the annotation. - if let Some(c) = tree.type_var_to_val_map.get(&rule_type_var) { - tree.type_var_to_val_map.insert(annotation_type_var, *c); - } - tree.var_constraints - .insert(TypeExpr::Variable(rule_type_var, annotation_type_var)); - } - - for (child, arg) in children.iter().zip(&annotation.sig.args) { - let annotation_type_var = annotation_info.var_to_type_var[&arg.name]; - let arg_name = format!( - "{}__{}__{}", - annotation_info.term, arg.name, annotation_type_var - ); - tree.quantified_vars - .insert((arg_name.clone(), annotation_type_var)); - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(child.clone()), - Box::new(veri_ir::Expr::Terminal(veri_ir::Terminal::Var(arg_name))), - ); - if rhs { - tree.rhs_assumptions.push(eq); - } else { - tree.lhs_assumptions.push(eq); - } - } - // set term ret var equal to annotation ret var - let ret_var = annotation_info.var_to_type_var[&annotation.sig.ret.name]; - tree.var_constraints - .insert(TypeExpr::Variable(curr.type_var, ret_var)); - let ret_name = format!( - "{}__{}__{}", - annotation_info.term, annotation.sig.ret.name, ret_var - ); - tree.quantified_vars.insert((ret_name.clone(), ret_var)); - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(veri_ir::Expr::Terminal(veri_ir::Terminal::Var( - curr.ident.clone(), - ))), - Box::new(veri_ir::Expr::Terminal(veri_ir::Terminal::Var(ret_name))), - ); - if rhs { - tree.rhs_assumptions.push(eq); - } else { - tree.lhs_assumptions.push(eq); - } - - annotation_infos.push(annotation_info); - Some(veri_ir::Expr::Terminal(veri_ir::Terminal::Var( - curr.ident.clone(), - ))) - } - }; - if let Some(e) = e { - tree.ty_vars.insert(e.clone(), curr.type_var); - Some(e) - } else { - None - } -} - -// Solve constraints as follows: -// - process concrete constraints first -// - then process variable constraints -// - constraints involving bv without widths are last priority -// -// for example: -// t2 = bv16 -// t3 = bv8 -// -// t5 = t4 -// t6 = t1 -// t3 = t4 -// t1 = t2 -// t7 = t8 -// -// t4 = bv -// t1 = bv -// t7 = bv -// -// would result in: -// bv16 -> t2, t6, t1 -// bv8 -> t3, t5, t4 -// poly(0) -> t5, t4 (intermediate group that gets removed) -// poly(1) -> t6, t1 (intermediate group that gets removed) -// poly(2) -> t7, t8 (intermediate group that gets removed) -// bv -> t7, t8 - -// TODO: clean up -fn solve_constraints( - concrete: HashSet, - var: HashSet, - bv: HashSet, - vals: &mut HashMap, - ty_vars: Option<&HashMap>, -) -> (HashMap, HashMap) { - // maintain a union find that maps types to sets of type vars that have that type - let mut union_find = HashMap::new(); - let mut poly = 0; - - let mut iterate = || { - // initialize union find with groups corresponding to concrete constraints - for c in &concrete { - match c { - TypeExpr::Concrete(v, t) => { - if !union_find.contains_key(t) { - union_find.insert(t.clone(), HashSet::new()); - } - if let Some(group) = union_find.get_mut(t) { - group.insert(*v); - } - } - TypeExpr::WidthInt(v, w) => { - if let Some(c) = vals.get(w) { - let width: usize = (*c).try_into().unwrap(); - let ty = annotation_ir::Type::BitVectorWithWidth(width); - if !union_find.contains_key(&ty) { - union_find.insert(ty.clone(), HashSet::new()); - } - if let Some(group) = union_find.get_mut(&ty) { - group.insert(*v); - } - } - } - _ => panic!("Non-concrete constraint found in concrete constraints: {c:#?}"), - }; - } - - // process variable constraints as follows: - // if t1 = t2 and only t1 has been typed, add t2 to the same set as t1 - // if t1 = t2 and only t2 has been typed, add t1 to the same set t2 - // if t1 = t2 and neither has been typed, create a new poly type and add both to the set - // if t1 = t2 and both have been typed, union appropriately - for v in &var { - match v { - TypeExpr::Variable(v1, v2) => { - let t1 = get_var_type(*v1, &union_find); - let t2 = get_var_type(*v2, &union_find); - - match (t1, t2) { - (Some(x), Some(y)) => { - match (x.is_poly(), y.is_poly()) { - (false, false) => { - if x != y { - let e1 = ty_vars.unwrap().iter().find_map(|(k, &v)| { - if v == *v1 { - Some(k) - } else { - None - } - }); - let e2 = ty_vars.unwrap().iter().find_map(|(k, &v)| { - if v == *v2 { - Some(k) - } else { - None - } - }); - match (e1, e2) { - (Some(e1), Some(e2)) => - panic!( - "type conflict\n\t{e1}\nhas type\n\t{x}\nbut\n\t{e2}\nhas type\n\t{y}" - ), - _ => continue, - } - } - } - // union t1 and t2, keeping t2 as the leader - (true, false) => { - let g1 = - union_find.remove(&x).expect("expected key in union find"); - let g2 = - union_find.get_mut(&y).expect("expected key in union find"); - g2.extend(g1.iter()); - } - // union t1 and t2, keeping t1 as the leader - (_, true) => { - // guard against the case where x and y have the same poly type - // so we remove the key and can't find it in the next line - if x != y { - let g2 = union_find - .remove(&y) - .expect("expected key in union find"); - let g1 = union_find - .get_mut(&x) - .expect("expected key in union find"); - g1.extend(g2.iter()); - } - } - }; - } - (Some(x), None) => { - if let Some(group) = union_find.get_mut(&x) { - group.insert(*v2); - } - } - (None, Some(x)) => { - if let Some(group) = union_find.get_mut(&x) { - group.insert(*v1); - } - } - (None, None) => { - let t = annotation_ir::Type::Poly(poly); - union_find.insert(t.clone(), HashSet::new()); - if let Some(group) = union_find.get_mut(&t) { - group.insert(*v1); - group.insert(*v2); - } - poly += 1; - } - } - } - _ => panic!("Non-variable constraint found in var constraints: {v:#?}"), - } - } - - for b in &bv { - match b { - TypeExpr::Concrete(v, ref t) => { - match t { - annotation_ir::Type::BitVector => { - // if there's a bv constraint and the var has already - // been typed (with a width), ignore the constraint - if let Some(var_type) = get_var_type_concrete(*v, &union_find) { - match var_type { - annotation_ir::Type::BitVectorWithWidth(_) => { - continue; - } - annotation_ir::Type::BitVectorUnknown(_) => { - continue; - } - _ => { - let e = ty_vars - .unwrap() - .iter() - .find_map( - |(k, &u)| if u == *v { Some(k) } else { None }, - ) - .unwrap(); - panic!("Var was already typed as {var_type:#?} but currently processing constraint: {b:#?}\n{e:?}") - } - } - - // otherwise add it to a generic bv bucket - } else { - // if !union_find.contains_key(t) { - // union_find.insert(t.clone(), HashSet::new()); - // } - // if let Some(group) = union_find.get_mut(t) { - // group.insert(v); - // } - let unknown_by_tyvar = annotation_ir::Type::BitVectorUnknown(*v); - let mut set = HashSet::new(); - set.insert(*v); - union_find.insert(unknown_by_tyvar.clone(), set); - - // if this type var also has a polymorphic type, union - if let Some(var_type) = get_var_type_poly(*v, &union_find) { - let poly_bucket = union_find - .remove(&var_type) - .expect("expected key in union find"); - let bv_bucket = union_find - .get_mut(&unknown_by_tyvar) - .expect("expected key in union find"); - bv_bucket.extend(poly_bucket.iter()); - } - } - } - _ => panic!("Non-bv constraint found in bv constraints: {b:#?}"), - } - } - TypeExpr::Variable(_, _) => { - panic!("Non-bv constraint found in bv constraints: {b:#?}") - } - TypeExpr::WidthInt(_, _) => { - panic!("Non-bv constraint found in bv constraints: {b:#?}") - } - } - } - for c in &concrete { - if let TypeExpr::WidthInt(v, w) = c { - if let Some(annotation_ir::Type::BitVectorWithWidth(width)) = - get_var_type_concrete(*v, &union_find) - { - vals.insert(*w, width as i128); - } - } - } - }; - - iterate(); - - let mut result = HashMap::new(); - let mut bv_unknown_width_sets = HashMap::new(); - let mut bv_unknown_width_idx = 0u32; - for (t, vars) in union_find { - for var in &vars { - result.insert(*var, t.clone()); - } - if matches!(t, annotation_ir::Type::BitVectorUnknown(..)) { - for var in &vars { - bv_unknown_width_sets.insert(*var, bv_unknown_width_idx); - } - bv_unknown_width_idx += 1; - } - } - (result, bv_unknown_width_sets) -} - -// if the union find already contains the type var, return its type -// otherwise return None -fn get_var_type( - t: u32, - u: &HashMap>, -) -> Option { - for (ty, vars) in u { - if vars.contains(&t) { - return Some(ty.clone()); - } - } - None -} - -// If the union find contains the type var and it has a non-polymorphic, specific type -// return it. Otherwise return None. -fn get_var_type_concrete( - t: u32, - u: &HashMap>, -) -> Option { - for (ty, vars) in u { - match ty { - annotation_ir::Type::Poly(_) | annotation_ir::Type::BitVector => continue, - _ => { - if vars.contains(&t) { - return Some(ty.clone()); - } - } - } - } - None -} - -// If the union find contains the type var and it has a polymorphic type, -// return the polymorphic type. Otherwise return None. -fn get_var_type_poly( - t: u32, - u: &HashMap>, -) -> Option { - for (ty, vars) in u { - match ty { - annotation_ir::Type::Poly(_) => { - if vars.contains(&t) { - return Some(ty.clone()); - } - } - _ => continue, - } - } - None -} - -fn annotation_type_for_vir_type(ty: &Type) -> annotation_ir::Type { - match ty { - Type::BitVector(Some(x)) => annotation_ir::Type::BitVectorWithWidth(*x), - Type::BitVector(None) => annotation_ir::Type::BitVector, - Type::Bool => annotation_ir::Type::Bool, - Type::Int => annotation_ir::Type::Int, - Type::Unit => annotation_ir::Type::Unit, - } -} - -fn create_parse_tree_pattern( - rule: &isle::sema::Rule, - pattern: &isle::sema::Pattern, - tree: &mut RuleParseTree, - typeenv: &TypeEnv, - termenv: &TermEnv, - term: &String, - types: &TermSignature, -) -> TypeVarNode { - match pattern { - isle::sema::Pattern::Term(_, term_id, args) => { - let sym = termenv.terms[term_id.index()].name; - let name = typeenv.syms[sym.index()].clone(); - - let mut assertions = vec![]; - // process children first - let mut children = vec![]; - for (i, arg) in args.iter().enumerate() { - let child = - create_parse_tree_pattern(rule, arg, tree, typeenv, termenv, term, types); - - // Our specified input term, use external types - if name.eq(term) { - tree.concrete_constraints.insert(TypeExpr::Concrete( - child.type_var, - annotation_type_for_vir_type(&types.args[i]), - )); - - // If this is a bitvector, mark the name for the assumption feasibility check - if let Type::BitVector(Some(w)) = &types.args[i] { - tree.term_input_bvs.push(child.ident.clone()); - - // Hack: width matching - let lit = veri_ir::Expr::Terminal(veri_ir::Terminal::Const(*w as i128, 0)); - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(veri_ir::Expr::WidthOf(Box::new(veri_ir::Expr::Terminal( - veri_ir::Terminal::Var(child.ident.clone()), - )))), - Box::new(lit), - ); - assertions.push(eq); - } - tree.term_args.push(child.ident.clone()) - } - children.push(child); - } - let type_var = tree.next_type_var; - tree.next_type_var += 1; - - if name.eq(term) { - tree.concrete_constraints.insert(TypeExpr::Concrete( - type_var, - annotation_type_for_vir_type(&types.ret), - )); - // Hack: width matching - if let Type::BitVector(Some(w)) = &types.ret { - let lit = veri_ir::Expr::Terminal(veri_ir::Terminal::Const(*w as i128, 0)); - let eq = veri_ir::Expr::Binary( - veri_ir::BinaryOp::Eq, - Box::new(veri_ir::Expr::WidthOf(Box::new(veri_ir::Expr::Terminal( - veri_ir::Terminal::Var(format!("{name}__{type_var}")), - )))), - Box::new(lit), - ); - assertions.push(eq); - } - } - - TypeVarNode { - ident: format!("{name}__{type_var}"), - construct: TypeVarConstruct::Term(*term_id), - type_var, - children, - assertions, - } - } - isle::sema::Pattern::Var(_, var_id) => { - let sym = rule.vars[var_id.index()].name; - let ident = typeenv.syms[sym.index()].clone(); - - let type_var = tree - .varid_to_type_var_map - .entry(*var_id) - .or_insert(tree.next_type_var); - if *type_var == tree.next_type_var { - tree.next_type_var += 1; - } - let ident = format!("{}__clif{}__{}", ident, var_id.index(), *type_var); - // this is a base case so there are no children - TypeVarNode { - ident, - construct: TypeVarConstruct::Var, - type_var: *type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Pattern::BindPattern(_, var_id, subpat) => { - let sym = rule.vars[var_id.index()].name; - - let type_var = *tree - .varid_to_type_var_map - .entry(*var_id) - .or_insert(tree.next_type_var); - if type_var == tree.next_type_var { - tree.next_type_var += 1; - } - - let ident = format!( - "{}__clif{}__{}", - typeenv.syms[sym.index()], - var_id.index(), - type_var - ); - - // this is a base case so there are no children - let var_node = TypeVarNode { - ident: ident.clone(), - construct: TypeVarConstruct::Var, - type_var, - children: vec![], - assertions: vec![], - }; - - let subpat_node = - create_parse_tree_pattern(rule, subpat, tree, typeenv, termenv, term, types); - - let bind_type_var = tree.next_type_var; - tree.next_type_var += 1; - - tree.var_constraints - .insert(TypeExpr::Variable(type_var, subpat_node.type_var)); - tree.var_constraints - .insert(TypeExpr::Variable(bind_type_var, type_var)); - tree.var_constraints - .insert(TypeExpr::Variable(bind_type_var, subpat_node.type_var)); - - TypeVarNode { - ident, - construct: TypeVarConstruct::BindPattern, - type_var, - children: vec![var_node, subpat_node], - assertions: vec![], - } - } - isle::sema::Pattern::Wildcard(_) => { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - TypeVarNode { - ident: format!("wildcard__{type_var}"), - construct: TypeVarConstruct::Wildcard(type_var), - type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Pattern::ConstPrim(_, sym) => { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - let name = typeenv.syms[sym.index()].clone(); - let val = match name.as_str() { - "I64" => 64, - "I32" => 32, - "I16" => 16, - "I8" => 8, - "true" => 1, - "false" => 0, - // Not currently used, but parsed - "I128" => 16, - _ => todo!("{:?}", &name), - }; - let name = format!("{name}__{type_var}"); - - TypeVarNode { - ident: name, - construct: TypeVarConstruct::Const(val), - type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Pattern::ConstBool(_, val) => { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - let name = format!("{val}__{type_var}"); - TypeVarNode { - ident: name, - construct: TypeVarConstruct::Bool(*val), - type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Pattern::ConstInt(_, num) => { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - let name = format!("{num}__{type_var}"); - TypeVarNode { - ident: name, - construct: TypeVarConstruct::Const(*num), - type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Pattern::And(_, subpats) => { - let mut children = vec![]; - let mut ty_vars = vec![]; - for p in subpats { - let child = create_parse_tree_pattern(rule, p, tree, typeenv, termenv, term, types); - ty_vars.push(child.type_var); - children.push(child); - } - let type_var = tree.next_type_var; - tree.next_type_var += 1; - - // Assert all sub type constraints are equivalent to the first subexpression - let first = ty_vars[0]; - for e in &ty_vars[1..] { - tree.var_constraints - .insert(TypeExpr::Variable(first, e.to_owned())); - } - - TypeVarNode { - ident: String::from("and"), - construct: TypeVarConstruct::And, - type_var, - children, - assertions: vec![], - } - } - } -} - -fn create_parse_tree_expr( - rule: &isle::sema::Rule, - expr: &isle::sema::Expr, - tree: &mut RuleParseTree, - typeenv: &TypeEnv, - termenv: &TermEnv, -) -> TypeVarNode { - match expr { - isle::sema::Expr::Term(_, term_id, args) => { - let sym = termenv.terms[term_id.index()].name; - let name = typeenv.syms[sym.index()].clone(); - - // process children first - let mut children = vec![]; - for arg in args { - let child = create_parse_tree_expr(rule, arg, tree, typeenv, termenv); - children.push(child); - } - let type_var = tree.next_type_var; - tree.next_type_var += 1; - - TypeVarNode { - ident: format!("{name}__{type_var}"), - construct: TypeVarConstruct::Term(*term_id), - type_var, - children, - assertions: vec![], - } - } - isle::sema::Expr::Var(_, var_id) => { - let mut ident = var_id.0.to_string(); - if var_id.index() < rule.vars.len() { - let sym = rule.vars[var_id.index()].name; - ident.clone_from(&typeenv.syms[sym.index()]) - } else { - println!("var {} not found, using var id instead", var_id.0); - ident = format!("v{ident}"); - } - - let type_var = tree - .varid_to_type_var_map - .entry(*var_id) - .or_insert(tree.next_type_var); - if *type_var == tree.next_type_var { - tree.next_type_var += 1; - } - let ident = format!("{}__clif{}__{}", ident, var_id.index(), *type_var); - // this is a base case so there are no children - TypeVarNode { - ident, - construct: TypeVarConstruct::Var, - type_var: *type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Expr::ConstPrim(_, sym) => { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - let name = typeenv.syms[sym.index()].clone(); - let val = match name.as_str() { - "I8" => 8, - "I16" => 16, - "I64" => 64, - "I32" => 32, - "false" => 0, - "true" => 1, - _ => todo!("{:?}", &name), - }; - let name = format!("{name}__{type_var}"); - TypeVarNode { - ident: name, - construct: TypeVarConstruct::Const(val), - type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Expr::ConstBool(_, val) => { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - let name = format!("{val}__{type_var}"); - TypeVarNode { - ident: name, - construct: TypeVarConstruct::Bool(*val), - type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Expr::ConstInt(_, num) => { - let type_var = tree.next_type_var; - tree.next_type_var += 1; - let name = format!("{num}__{type_var}"); - TypeVarNode { - ident: name, - construct: TypeVarConstruct::Const(*num), - type_var, - children: vec![], - assertions: vec![], - } - } - isle::sema::Expr::Let { bindings, body, .. } => { - let mut children = vec![]; - let mut bound = vec![]; - for (varid, _, expr) in bindings { - let sym = rule.vars[varid.index()].name; - let var = typeenv.syms[sym.index()].clone(); - let subpat_node = create_parse_tree_expr(rule, expr, tree, typeenv, termenv); - - let ty_var = tree.next_type_var; - tree.next_type_var += 1; - - tree.var_constraints - .insert(TypeExpr::Variable(ty_var, subpat_node.type_var)); - - tree.varid_to_type_var_map.insert(*varid, ty_var); - children.push(subpat_node); - let ident = format!("{}__clif{}__{}", var, varid.index(), ty_var); - tree.quantified_vars.insert((ident.clone(), ty_var)); - bound.push(ident); - } - let body = create_parse_tree_expr(rule, body, tree, typeenv, termenv); - let body_var = body.type_var; - children.push(body); - - let type_var = tree.next_type_var; - tree.next_type_var += 1; - - let name = format!("let__{type_var}"); - - // The let should have the same type as the body - tree.var_constraints - .insert(TypeExpr::Variable(type_var, body_var)); - - TypeVarNode { - ident: name, - construct: TypeVarConstruct::Let(bound), - type_var, - children, - assertions: vec![], - } - } - } -} - -// TODO mod tests? -#[test] -fn test_solve_constraints() { - // simple with specific and generic bvs - let concrete = HashSet::from([ - TypeExpr::Concrete(2, annotation_ir::Type::BitVectorWithWidth(16)), - TypeExpr::Concrete(3, annotation_ir::Type::BitVectorWithWidth(8)), - ]); - let var = HashSet::from([ - TypeExpr::Variable(5, 4), - TypeExpr::Variable(6, 1), - TypeExpr::Variable(3, 4), - TypeExpr::Variable(1, 2), - ]); - let bv = HashSet::from([ - TypeExpr::Concrete(1, annotation_ir::Type::BitVector), - TypeExpr::Concrete(4, annotation_ir::Type::BitVector), - ]); - let expected = HashMap::from([ - (1, annotation_ir::Type::BitVectorWithWidth(16)), - (2, annotation_ir::Type::BitVectorWithWidth(16)), - (3, annotation_ir::Type::BitVectorWithWidth(8)), - (4, annotation_ir::Type::BitVectorWithWidth(8)), - (5, annotation_ir::Type::BitVectorWithWidth(8)), - (6, annotation_ir::Type::BitVectorWithWidth(16)), - ]); - let (sol, bvsets) = solve_constraints(concrete, var, bv, &mut HashMap::new(), None); - assert_eq!(expected, sol); - assert!(bvsets.is_empty()); - - // slightly more complicated with specific and generic bvs - let concrete = HashSet::from([ - TypeExpr::Concrete(2, annotation_ir::Type::BitVectorWithWidth(16)), - TypeExpr::Concrete(3, annotation_ir::Type::BitVectorWithWidth(8)), - ]); - let var = HashSet::from([ - TypeExpr::Variable(5, 4), - TypeExpr::Variable(6, 1), - TypeExpr::Variable(3, 4), - TypeExpr::Variable(1, 2), - TypeExpr::Variable(7, 8), - ]); - let bv = HashSet::from([ - TypeExpr::Concrete(1, annotation_ir::Type::BitVector), - TypeExpr::Concrete(4, annotation_ir::Type::BitVector), - TypeExpr::Concrete(7, annotation_ir::Type::BitVector), - ]); - let expected = HashMap::from([ - (1, annotation_ir::Type::BitVectorWithWidth(16)), - (2, annotation_ir::Type::BitVectorWithWidth(16)), - (3, annotation_ir::Type::BitVectorWithWidth(8)), - (4, annotation_ir::Type::BitVectorWithWidth(8)), - (5, annotation_ir::Type::BitVectorWithWidth(8)), - (6, annotation_ir::Type::BitVectorWithWidth(16)), - (7, annotation_ir::Type::BitVectorUnknown(7)), - (8, annotation_ir::Type::BitVectorUnknown(7)), - ]); - let expected_bvsets = HashMap::from([(7, 0), (8, 0)]); - let (sol, bvsets) = solve_constraints(concrete, var, bv, &mut HashMap::new(), None); - assert_eq!(expected, sol); - assert_eq!(expected_bvsets, bvsets); -} - -#[test] -#[should_panic] -fn test_solve_constraints_ill_typed() { - // ill-typed - let concrete = HashSet::from([ - TypeExpr::Concrete(2, annotation_ir::Type::BitVectorWithWidth(16)), - TypeExpr::Concrete(3, annotation_ir::Type::BitVectorWithWidth(8)), - ]); - let var = HashSet::from([ - TypeExpr::Variable(5, 4), - TypeExpr::Variable(6, 1), - TypeExpr::Variable(4, 6), - TypeExpr::Variable(3, 4), - TypeExpr::Variable(1, 2), - ]); - let bv = HashSet::from([ - TypeExpr::Concrete(1, annotation_ir::Type::BitVector), - TypeExpr::Concrete(4, annotation_ir::Type::BitVector), - ]); - solve_constraints(concrete, var, bv, &mut HashMap::new(), None); -} diff --git a/cranelift/isle/veri/veri_engine/src/verify.rs b/cranelift/isle/veri/veri_engine/src/verify.rs deleted file mode 100644 index 915240475853..000000000000 --- a/cranelift/isle/veri/veri_engine/src/verify.rs +++ /dev/null @@ -1,136 +0,0 @@ -use crate::type_inference::type_rules_with_term_and_types; -use crate::Config; -use cranelift_isle::error::Errors; -use cranelift_isle::{self as isle}; -use isle::compile::create_envs; -use isle::sema::{Pattern, RuleId, TermEnv, TypeEnv}; -use std::collections::HashMap; -use std::path::PathBuf; - -use crate::annotations::parse_annotations; -use crate::solver::run_solver; -use crate::type_inference::RuleSemantics; -use crate::{interp::Context, termname::pattern_contains_termname}; -use veri_ir::{ConcreteTest, TermSignature, VerificationResult}; - -pub fn verify_rules( - inputs: Vec, - config: &Config, - widths: &Option>, -) -> Result<(), Errors> { - // Produces environments including terms, rules, and maps from symbols and - // names to types - let (typeenv, termenv, defs) = create_envs(inputs).unwrap(); - - let annotation_env = parse_annotations(&defs, &termenv, &typeenv); - - // Get the types/widths for this particular term - let types = annotation_env - .get_term_signatures_by_name(&termenv, &typeenv) - .get(&config.term as &str) - .unwrap_or_else(|| panic!("Missing term type instantiation for {}", config.term)) - .clone(); - - let types_filtered = if let Some(widths) = widths { - let mut width_types = Vec::new(); - - for w in widths { - let width_type = match w.as_str() { - "I8" => veri_ir::Type::BitVector(Some(8)), - "I16" => veri_ir::Type::BitVector(Some(16)), - "I32" => veri_ir::Type::BitVector(Some(32)), - "I64" => veri_ir::Type::BitVector(Some(64)), - _ => panic!("Invalid width type: {w}"), - }; - width_types.push(width_type); - } - - types - .into_iter() - .filter(|t| { - if let Some(canonical_type) = &t.canonical_type { - width_types.contains(canonical_type) - } else { - false - } - }) - .collect::>() - } else { - types - }; - - for type_instantiation in types_filtered { - let type_sols = type_rules_with_term_and_types( - &termenv, - &typeenv, - &annotation_env, - config, - &type_instantiation, - &None, - ); - verify_rules_for_term( - &termenv, - &typeenv, - &type_sols, - type_instantiation, - &None, - config, - ); - } - Ok(()) -} - -pub fn verify_rules_for_term( - termenv: &TermEnv, - typeenv: &TypeEnv, - typesols: &HashMap, - types: TermSignature, - concrete: &Option, - config: &Config, -) -> VerificationResult { - let mut rules_checked = 0; - for rule in &termenv.rules { - // Only type rules with the given term on the LHS - if !pattern_contains_termname( - // Hack for now: typeid not used - &Pattern::Term( - cranelift_isle::sema::TypeId(0), - rule.root_term, - rule.args.clone(), - ), - &config.term, - termenv, - typeenv, - ) { - continue; - } - if let Some(names) = &config.names { - if rule.name.is_none() { - continue; - } - let name = &typeenv.syms[rule.name.unwrap().index()]; - if !names.contains(name) { - continue; - } else { - log::debug!("Verifying rule: {name}"); - } - } - let ctx = Context::new(typesols); - if ctx.typesols.get(&rule.id).is_none() { - continue; - } - let rule_sem = &ctx.typesols[&rule.id]; - log::debug!("Term: {}", config.term); - log::debug!("Type instantiation: {types}"); - let result = run_solver(rule_sem, rule, termenv, typeenv, concrete, config, &types); - rules_checked += 1; - if result != VerificationResult::Success { - return result; - } - } - if rules_checked > 0 { - VerificationResult::Success - } else { - panic!("No rules checked!") - } -} diff --git a/cranelift/isle/veri/veri_engine/tests/utils/mod.rs b/cranelift/isle/veri/veri_engine/tests/utils/mod.rs deleted file mode 100644 index 7c621f90956c..000000000000 --- a/cranelift/isle/veri/veri_engine/tests/utils/mod.rs +++ /dev/null @@ -1,341 +0,0 @@ -use cranelift_codegen_meta::{ - generate_isle, - isle::{get_isle_compilations, shared_isle_lower_paths}, -}; -use cranelift_isle::compile::create_envs; -use std::env; -use std::path::PathBuf; -use strum::IntoEnumIterator; -use strum_macros::EnumIter; -use veri_engine_lib::annotations::parse_annotations; -use veri_engine_lib::type_inference::type_rules_with_term_and_types; -use veri_engine_lib::verify::verify_rules_for_term; -use veri_engine_lib::Config; -use veri_ir::{ConcreteTest, Counterexample, TermSignature, VerificationResult}; - -#[derive(Debug, EnumIter, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] -#[repr(usize)] -pub enum Bitwidth { - I8 = 8, - I16 = 16, - I32 = 32, - I64 = 64, -} - -pub enum TestResult { - Simple(Vec<(Bitwidth, VerificationResult)>), - Expect(fn(&TermSignature) -> VerificationResult), -} - -type TestResultBuilder = dyn Fn(Bitwidth) -> (Bitwidth, VerificationResult); - -use std::sync::Once; - -static INIT: Once = Once::new(); - -pub fn get_isle_files(name: &str) -> Vec { - let cur_dir = env::current_dir().expect("Can't access current working directory"); - let gen_dir = cur_dir.join("test_output"); - INIT.call_once(|| { - // Logger - env_logger::init(); - // Test directory - if !gen_dir.is_dir() { - std::fs::create_dir(gen_dir.as_path()).unwrap(); - } - // Generate ISLE files. - generate_isle(gen_dir.as_path()).expect("Can't generate ISLE"); - }); - - let codegen_crate_dir = cur_dir.join("../../../codegen"); - let inst_specs_isle = codegen_crate_dir.join("src").join("inst_specs.isle"); - - match name { - "shared_lower" => { - let mut shared = shared_isle_lower_paths(codegen_crate_dir.as_path()); - shared.push(gen_dir.join("clif_lower.isle")); - shared - } - _ => { - // Lookup ISLE shared . - let compilations = - get_isle_compilations(codegen_crate_dir.as_path(), gen_dir.as_path()); - - // Return inputs from the matching compilation, if any. - let mut inputs = compilations.lookup(name).unwrap().inputs(); - inputs.push(inst_specs_isle); - inputs - } - } -} - -// Some examples of functions we might need -#[allow(dead_code)] -pub fn just_8_result() -> TestResult { - TestResult::Simple(vec![(Bitwidth::I8, VerificationResult::Success)]) -} - -#[allow(dead_code)] -pub fn just_16_result() -> TestResult { - TestResult::Simple(vec![(Bitwidth::I16, VerificationResult::Success)]) -} - -#[allow(dead_code)] -pub fn just_32_result() -> TestResult { - TestResult::Simple(vec![(Bitwidth::I32, VerificationResult::Success)]) -} - -#[allow(dead_code)] -pub fn just_64_result() -> TestResult { - TestResult::Simple(vec![(Bitwidth::I64, VerificationResult::Success)]) -} - -/// All bitwidths verify -#[allow(dead_code)] -pub fn all_success_result() -> Vec<(Bitwidth, VerificationResult)> { - custom_result(&|w| (w, VerificationResult::Success)) -} - -/// All bitwidths fail -#[allow(dead_code)] -pub fn all_failure_result() -> Vec<(Bitwidth, VerificationResult)> { - custom_result(&|w| (w, VerificationResult::Failure(Counterexample {}))) -} - -/// Specify a custom set expected result (helpful if you want to test all the bitwidths and expect -/// a range of different success, failure, and inapplicable outcomes) -pub fn custom_result(f: &TestResultBuilder) -> Vec<(Bitwidth, VerificationResult)> { - Bitwidth::iter().map(f).collect() -} - -fn test_rules_with_term(inputs: Vec, tr: TestResult, config: Config) { - let (typeenv, termenv, defs) = create_envs(inputs).unwrap(); - let annotation_env = parse_annotations(&defs, &termenv, &typeenv); - - let term_signatures = annotation_env - .get_term_signatures_by_name(&termenv, &typeenv) - .get(config.term.as_str()) - .unwrap_or_else(|| panic!("Missing term type instantiation for {}", config.term)) - .clone(); - let instantiations = match tr { - TestResult::Simple(s) => { - let mut res = vec![]; - for (width, result) in s { - let ty = match width { - Bitwidth::I8 => veri_ir::Type::BitVector(Some(8)), - Bitwidth::I16 => veri_ir::Type::BitVector(Some(16)), - Bitwidth::I32 => veri_ir::Type::BitVector(Some(32)), - Bitwidth::I64 => veri_ir::Type::BitVector(Some(64)), - }; - // Find the type instantiations with this as the canonical type - let all_instantiations: Vec<&TermSignature> = term_signatures - .iter() - .filter(|sig| sig.canonical_type.unwrap() == ty) - .collect(); - if all_instantiations.is_empty() { - panic!("Missing type instantiation for width {width:?}"); - } - for i in all_instantiations { - res.push((i.clone(), result.clone())); - } - } - res - } - TestResult::Expect(expect) => term_signatures - .iter() - .map(|sig| (sig.clone(), expect(sig))) - .collect(), - }; - - for (type_instantiation, expected_result) in instantiations { - log::debug!("Expected result: {expected_result:?}"); - let type_sols = type_rules_with_term_and_types( - &termenv, - &typeenv, - &annotation_env, - &config, - &type_instantiation, - &None, - ); - let result = verify_rules_for_term( - &termenv, - &typeenv, - &type_sols, - type_instantiation, - &None, - &config, - ); - assert_eq!(result, expected_result); - } -} - -pub fn test_from_file_with_lhs_termname_simple( - file: &str, - termname: String, - tr: Vec<(Bitwidth, VerificationResult)>, -) { - test_from_file_with_lhs_termname(file, termname, TestResult::Simple(tr)) -} - -pub fn test_from_file_with_lhs_termname(file: &str, termname: String, tr: TestResult) { - println!("Verifying {termname} rules in file: {file}"); - let mut inputs = get_isle_files("shared_lower"); - inputs.push(PathBuf::from(file)); - let config = Config { - term: termname, - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: None, - names: None, - }; - test_rules_with_term(inputs, tr, config); -} - -pub fn test_aarch64_rule_with_lhs_termname_simple( - rulename: &str, - termname: &str, - tr: Vec<(Bitwidth, VerificationResult)>, -) { - test_aarch64_rule_with_lhs_termname(rulename, termname, TestResult::Simple(tr)) -} - -pub fn test_aarch64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult) { - println!("Verifying rule `{rulename}` with termname {termname} "); - let inputs = get_isle_files("aarch64"); - let config = Config { - term: termname.to_string(), - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: None, - names: Some(vec![rulename.to_string()]), - }; - test_rules_with_term(inputs, tr, config); -} - -pub fn test_x64_rule_with_lhs_termname_simple( - rulename: &str, - termname: &str, - tr: Vec<(Bitwidth, VerificationResult)>, -) { - test_x64_rule_with_lhs_termname(rulename, termname, TestResult::Simple(tr)) -} - -pub fn test_x64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult) { - println!("Verifying rule `{rulename}` with termname {termname} "); - let inputs = get_isle_files("x64"); - let config = Config { - term: termname.to_string(), - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: None, - names: Some(vec![rulename.to_string()]), - }; - test_rules_with_term(inputs, tr, config); -} - -pub fn test_from_file_with_config_simple( - file: &str, - config: Config, - tr: Vec<(Bitwidth, VerificationResult)>, -) { - test_from_file_with_config(file, config, TestResult::Simple(tr)) -} -pub fn test_from_file_with_config(file: &str, config: Config, tr: TestResult) { - println!("Verifying {} rules in file: {}", config.term, file); - let mut inputs = get_isle_files("shared_lower"); - inputs.push(PathBuf::from(file)); - test_rules_with_term(inputs, tr, config); -} - -pub fn test_aarch64_with_config_simple(config: Config, tr: Vec<(Bitwidth, VerificationResult)>) { - test_aarch64_with_config(config, TestResult::Simple(tr)) -} - -pub fn test_aarch64_with_config(config: Config, tr: TestResult) { - println!( - "Verifying rules {:?} with termname {}", - config.names, config.term - ); - let inputs = get_isle_files("aarch64"); - test_rules_with_term(inputs, tr, config); -} - -pub fn test_concrete_aarch64_rule_with_lhs_termname( - rulename: &str, - termname: &str, - concrete: ConcreteTest, -) { - println!("Verifying concrete input rule `{rulename}` with termname {termname} "); - let inputs = get_isle_files("aarch64"); - let (typeenv, termenv, defs) = create_envs(inputs).unwrap(); - let annotation_env = parse_annotations(&defs, &termenv, &typeenv); - - let config = Config { - term: termname.to_string(), - distinct_check: false, - custom_verification_condition: None, - custom_assumptions: None, - names: Some(vec![rulename.to_string()]), - }; - - // Get the types/widths for this particular term - let args = concrete.args.iter().map(|i| i.ty).collect(); - let ret = concrete.output.ty; - let t = TermSignature { - args, - ret, - canonical_type: None, - }; - - let type_sols = type_rules_with_term_and_types( - &termenv, - &typeenv, - &annotation_env, - &config, - &t, - &Some(concrete.clone()), - ); - let result = verify_rules_for_term(&termenv, &typeenv, &type_sols, t, &Some(concrete), &config); - assert_eq!(result, VerificationResult::Success); -} - -pub fn test_concrete_input_from_file_with_lhs_termname( - file: &str, - termname: String, - concrete: ConcreteTest, -) { - println!("Verifying concrete input {termname} rule in file: {file}"); - let mut inputs = get_isle_files("shared_lower"); - inputs.push(PathBuf::from(file)); - - let (typeenv, termenv, defs) = create_envs(inputs).unwrap(); - let annotation_env = parse_annotations(&defs, &termenv, &typeenv); - - let config = Config { - term: termname.clone(), - distinct_check: false, - custom_verification_condition: None, - custom_assumptions: None, - names: None, - }; - - // Get the types/widths for this particular term - let args = concrete.args.iter().map(|i| i.ty).collect(); - let ret = concrete.output.ty; - let t = TermSignature { - args, - ret, - canonical_type: None, - }; - - let type_sols = type_rules_with_term_and_types( - &termenv, - &typeenv, - &annotation_env, - &config, - &t, - &Some(concrete.clone()), - ); - let result = verify_rules_for_term(&termenv, &typeenv, &type_sols, t, &Some(concrete), &config); - assert_eq!(result, VerificationResult::Success); -} diff --git a/cranelift/isle/veri/veri_engine/tests/veri.rs b/cranelift/isle/veri/veri_engine/tests/veri.rs deleted file mode 100644 index 4fe0f83545e2..000000000000 --- a/cranelift/isle/veri/veri_engine/tests/veri.rs +++ /dev/null @@ -1,3224 +0,0 @@ -mod utils; -use utils::{all_failure_result, all_success_result}; -use utils::{ - test_aarch64_rule_with_lhs_termname_simple, test_aarch64_with_config_simple, - test_concrete_aarch64_rule_with_lhs_termname, test_concrete_input_from_file_with_lhs_termname, - test_from_file_with_config_simple, test_from_file_with_lhs_termname, - test_from_file_with_lhs_termname_simple, test_x64_rule_with_lhs_termname_simple, Bitwidth, - TestResult, -}; -use veri_engine_lib::Config; -use veri_ir::{ConcreteInput, ConcreteTest, Counterexample, VerificationResult}; - -#[test] -fn test_named_iadd_base_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "iadd_base_case", - "iadd", - ConcreteTest { - termname: "iadd".to_string(), - args: vec![ - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ], - output: ConcreteInput { - literal: "#b00000010".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_named_iadd_base() { - test_aarch64_rule_with_lhs_termname_simple("iadd_base_case", "iadd", all_success_result()) -} - -#[test] -fn test_named_iadd_imm12_right() { - test_aarch64_rule_with_lhs_termname_simple("iadd_imm12_right", "iadd", all_success_result()) -} - -#[test] -fn test_named_iadd_imm12_left() { - test_aarch64_rule_with_lhs_termname_simple("iadd_imm12_left", "iadd", all_success_result()) -} - -#[test] -fn test_named_iadd_imm12_neg_left() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_imm12_neg_left", - "iadd", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_iadd_imm12_neg_right() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_imm12_neg_right", - "iadd", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -// Need a file test because this is a change on top of our latest rebase -#[test] -fn test_named_imm12_from_negated_value() { - test_aarch64_rule_with_lhs_termname_simple( - "imm12_from_negated_value", - "imm12_from_negated_value", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -// Need a file test because this is a change on top of our latest rebase -#[test] -fn test_updated_iadd_imm12neg_right() { - test_from_file_with_lhs_termname_simple( - "./examples/iadd/updated_iadd_imm12neg_right.isle", - "iadd".to_string(), - all_success_result(), - ) -} - -// Need a file test because this is a change on top of our latest rebase -#[test] -fn test_updated_iadd_imm12neg_left() { - test_from_file_with_lhs_termname_simple( - "./examples/iadd/updated_iadd_imm12neg_left.isle", - "iadd".to_string(), - all_success_result(), - ) -} - -#[test] -fn test_named_iadd_extend_right() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_extend_right", - "iadd", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_iadd_extend_right_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "iadd_extend_right", - "iadd", - ConcreteTest { - termname: "iadd".to_string(), - args: vec![ - ConcreteInput { - literal: "#b0000000000000001".to_string(), - ty: veri_ir::Type::BitVector(Some(16)), - }, - ConcreteInput { - literal: "#b1111111111111111".to_string(), - ty: veri_ir::Type::BitVector(Some(16)), - }, - ], - output: ConcreteInput { - literal: "#b0000000000000000".to_string(), - ty: veri_ir::Type::BitVector(Some(16)), - }, - }, - ); - test_concrete_aarch64_rule_with_lhs_termname( - "iadd_extend_right", - "iadd", - ConcreteTest { - termname: "iadd".to_string(), - args: vec![ - ConcreteInput { - literal: "#b01000000000000000000000000000000".to_string(), - ty: veri_ir::Type::BitVector(Some(32)), - }, - ConcreteInput { - literal: "#b00000000000000001111111111111111".to_string(), - ty: veri_ir::Type::BitVector(Some(32)), - }, - ], - output: ConcreteInput { - literal: "#b01000000000000001111111111111111".to_string(), - ty: veri_ir::Type::BitVector(Some(32)), - }, - }, - ) -} - -#[test] -fn test_named_iadd_extend_left() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_extend_left", - "iadd", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_iadd_extend() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_add_extend.isle", - "iadd".to_string(), - vec![ - // The type of the iadd is the destination type, so for i8 there is no bad extend-to - (Bitwidth::I8, VerificationResult::Success), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_iadd_ishl_left() { - test_aarch64_rule_with_lhs_termname_simple("iadd_ishl_left", "iadd", all_success_result()) -} - -#[test] -fn test_named_iadd_ishl_right() { - test_aarch64_rule_with_lhs_termname_simple("iadd_ishl_right", "iadd", all_success_result()) -} - -#[test] -fn test_named_iadd_imul_right() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_imul_right", - "iadd", - vec![ - (Bitwidth::I8, VerificationResult::Success), - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_iadd_imul_right() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_imul_right", - "iadd", - vec![ - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_named_iadd_imul_left() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_imul_left", - "iadd", - vec![ - (Bitwidth::I8, VerificationResult::Success), - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_iadd_imul_left() { - test_aarch64_rule_with_lhs_termname_simple( - "iadd_imul_left", - "iadd", - vec![ - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_named_isub_imul() { - test_aarch64_rule_with_lhs_termname_simple( - "isub_imul", - "isub", - vec![ - (Bitwidth::I8, VerificationResult::Success), - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_isub_imul() { - test_aarch64_rule_with_lhs_termname_simple( - "isub_imul", - "isub", - vec![ - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_broken_iadd_base_case() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_base_case.isle", - "iadd".to_string(), - all_failure_result(), - ) -} - -#[test] -fn test_broken_iadd_imm12() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_imm12.isle", - "iadd".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Success), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_iadd_imm12_2() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_imm12_2.isle", - "iadd".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_iadd_imm12neg_not_distinct() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_imm12neg.isle", - "iadd".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::NoDistinctModels), - (Bitwidth::I16, VerificationResult::NoDistinctModels), - (Bitwidth::I32, VerificationResult::NoDistinctModels), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_iadd_imm12neg_2_not_distinct() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_imm12neg2.isle", - "iadd".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::NoDistinctModels), - (Bitwidth::I16, VerificationResult::NoDistinctModels), - (Bitwidth::I32, VerificationResult::NoDistinctModels), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_iadd_imul_right() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_madd.isle", - "iadd".to_string(), - all_failure_result(), - ) -} - -#[test] -fn test_broken_iadd_imul_left() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_madd2.isle", - "iadd".to_string(), - all_failure_result(), - ) -} - -#[test] -fn test_broken_iadd_msub() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_msub.isle", - "isub".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_iadd_shift() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_shift.isle", - "iadd".to_string(), - all_failure_result(), - ) -} - -#[test] -fn test_broken_iadd_shift2() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/iadd/broken_shift2.isle", - "iadd".to_string(), - all_failure_result(), - ) -} - -#[test] -fn test_named_isub_base_case() { - test_aarch64_rule_with_lhs_termname_simple("isub_base_case", "isub", all_success_result()) -} - -#[test] -fn test_named_isub_imm12() { - test_aarch64_rule_with_lhs_termname_simple("isub_imm12", "isub", all_success_result()) -} - -#[test] -fn test_named_isub_imm12_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "isub_imm12", - "isub", - ConcreteTest { - termname: "isub".to_string(), - args: vec![ - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b11111111".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ], - output: ConcreteInput { - literal: "#b00000010".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_named_isub_imm12_neg() { - test_aarch64_rule_with_lhs_termname_simple( - "isub_imm12_neg", - "isub", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ); -} - -// The older version, which did not have distinct models for i8, i16, or i32. -#[test] -fn test_isub_imm12_neg_not_distinct() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/isub/broken_imm12neg_not_distinct.isle", - "isub".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::NoDistinctModels), - (Bitwidth::I16, VerificationResult::NoDistinctModels), - (Bitwidth::I32, VerificationResult::NoDistinctModels), - (Bitwidth::I64, VerificationResult::Success), - ], - ); -} - -#[test] -fn test_isub_imm12_neg_not_distinct_16_32() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/isub/broken_imm12neg_not_distinct.isle", - "isub".to_string(), - vec![ - (Bitwidth::I16, VerificationResult::NoDistinctModels), - (Bitwidth::I32, VerificationResult::NoDistinctModels), - ], - ); -} - -// Need a file test because this is a change on top of our latest rebase -#[test] -fn test_isub_imm12neg_new() { - test_from_file_with_lhs_termname_simple( - "./examples/isub/imm12neg_new.isle", - "isub".to_string(), - all_success_result(), - ); -} - -#[test] -fn test_named_isub_imm12_neg_concrete32() { - test_concrete_aarch64_rule_with_lhs_termname( - "isub_imm12_neg", - "isub", - ConcreteTest { - termname: "isub".to_string(), - args: vec![ - ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000001" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - ConcreteInput { - literal: "#b1111111111111111111111111111111111111111111111111111111111111111" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - ], - output: ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000010" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - }, - ) -} - -#[test] -fn test_named_isub_imm12_neg_concrete64() { - test_concrete_aarch64_rule_with_lhs_termname( - "isub_imm12_neg", - "isub", - ConcreteTest { - termname: "isub".to_string(), - args: vec![ - ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000001" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - ConcreteInput { - literal: "#b1111111111111111111111111111111111111111111111111111111111111111" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - ], - output: ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000010" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - }, - ) -} - -#[test] -fn test_named_isub_extend() { - test_aarch64_rule_with_lhs_termname_simple( - "isub_extend", - "isub", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_isub_ishl() { - test_aarch64_rule_with_lhs_termname_simple("isub_ishl", "isub", all_success_result()) -} - -#[test] -fn test_broken_isub_base_case() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/isub/broken_base_case.isle", - "isub".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ); -} - -#[test] -fn test_broken_isub_imm12() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/isub/broken_imm12.isle", - "isub".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Success), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ); -} - -#[test] -fn test_broken_isub_imm12neg_not_distinct() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/isub/broken_imm12neg.isle", - "isub".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::NoDistinctModels), - (Bitwidth::I16, VerificationResult::NoDistinctModels), - (Bitwidth::I32, VerificationResult::NoDistinctModels), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ); -} - -#[test] -fn test_broken_isub_shift() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/isub/broken_shift.isle", - "isub".to_string(), - all_failure_result(), - ); -} - -#[test] -fn test_named_ineg_base_case() { - test_aarch64_rule_with_lhs_termname_simple("ineg_base_case", "ineg", all_success_result()) -} - -#[test] -fn test_named_imul_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "imul_base_case", - "imul", - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - vec![ - (Bitwidth::I8, VerificationResult::Success), - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_imul_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "imul_base_case", - "imul", - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - vec![ - // (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -// TODO traps https://github.com/avanhatt/wasmtime/issues/31 -#[test] -fn test_named_udiv() { - test_aarch64_rule_with_lhs_termname_simple( - "udiv", - "udiv", - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - vec![ - (Bitwidth::I8, VerificationResult::Success), - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_udiv() { - test_aarch64_rule_with_lhs_termname_simple( - "udiv", - "udiv", - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - vec![ - // (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_broken_udiv() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/udiv/broken_udiv.isle", - "udiv".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_sdiv_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "sdiv_base_case", - "sdiv", - vec![ - (Bitwidth::I8, VerificationResult::Success), - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_sdiv_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "sdiv_base_case", - "sdiv", - vec![ - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_named_sdiv_safe_divisor() { - test_aarch64_rule_with_lhs_termname_simple( - "sdiv_safe_divisor", - "sdiv", - vec![ - (Bitwidth::I8, VerificationResult::Success), - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_sdiv_safe_divisor() { - test_aarch64_rule_with_lhs_termname_simple( - "sdiv_safe_divisor", - "sdiv", - vec![ - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_broken_sdiv_safe_const() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/sdiv/broken_sdiv_safe_const.isle", - "sdiv".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_sdiv() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/sdiv/broken_sdiv.isle", - "sdiv".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_srem() { - test_aarch64_rule_with_lhs_termname_simple( - "srem", - "srem", - vec![ - (Bitwidth::I8, VerificationResult::Success), - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_srem() { - test_aarch64_rule_with_lhs_termname_simple( - "srem", - "srem", - vec![ - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_named_urem() { - test_aarch64_rule_with_lhs_termname_simple( - "urem", - "urem", - vec![ - (Bitwidth::I8, VerificationResult::Success), - // Too slow right now: https://github.com/avanhatt/wasmtime/issues/36 - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -#[ignore] -fn test_named_slow_urem() { - test_aarch64_rule_with_lhs_termname_simple( - "urem", - "urem", - vec![ - (Bitwidth::I16, VerificationResult::Unknown), - (Bitwidth::I32, VerificationResult::Unknown), - (Bitwidth::I64, VerificationResult::Unknown), - ], - ) -} - -#[test] -fn test_named_urem_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "urem", - "urem", - ConcreteTest { - termname: "urem".to_string(), - args: vec![ - ConcreteInput { - literal: "#b11111110".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00110001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ], - output: ConcreteInput { - literal: "#b00001001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_named_uextend() { - test_aarch64_rule_with_lhs_termname_simple("uextend", "uextend", all_success_result()) -} - -#[test] -fn test_named_sextend() { - test_aarch64_rule_with_lhs_termname_simple("sextend", "sextend", all_success_result()) -} - -#[test] -fn test_broken_uextend() { - test_from_file_with_lhs_termname( - "./examples/broken/broken_uextend.isle", - "uextend".to_string(), - TestResult::Expect(|sig| { - // In the spec for extend, zero_extend and sign_extend are swapped. - // However, this should still succeed if the input and output - // widths are the same - if sig.args[0] == sig.ret { - VerificationResult::Success - } else { - VerificationResult::Failure(Counterexample {}) - } - }), - ); -} - -// AVH TODO: this rule requires priorities to be correct for narrow cases -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_clz_32_64() { - test_aarch64_rule_with_lhs_termname_simple( - "clz_32_64", - "clz", - vec![ - // (Bitwidth::I8, VerificationResult::InapplicableRule), - // (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_clz_8() { - test_aarch64_rule_with_lhs_termname_simple( - "clz_8", - "clz", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_clz_16() { - test_aarch64_rule_with_lhs_termname_simple( - "clz_16", - "clz", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_clz() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/clz/broken_clz.isle", - "clz".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_broken_clz8() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/clz/broken_clz8.isle", - "clz".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_clz_n6() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/clz/broken_clz16.isle", - "clz".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -// AVH TODO: this rule requires priorities to be correct for narrow cases -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_cls_32_64() { - test_aarch64_rule_with_lhs_termname_simple( - "cls_32_64", - "cls", - vec![ - // (Bitwidth::I8, VerificationResult::InapplicableRule), - // (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_cls_8() { - test_aarch64_rule_with_lhs_termname_simple( - "cls_8", - "cls", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_cls_16() { - test_aarch64_rule_with_lhs_termname_simple( - "cls_16", - "cls", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_cls_32_64() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/cls/broken_cls.isle", - "cls".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_broken_cls_8() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/cls/broken_cls8.isle", - "cls".to_string(), - vec![(Bitwidth::I8, VerificationResult::Failure(Counterexample {}))], - ) -} - -#[test] -fn test_broken_cls_16() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/cls/broken_cls16.isle", - "cls".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_ctz_32_64() { - test_aarch64_rule_with_lhs_termname_simple( - "ctz_32_64", - "ctz", - vec![ - // (Bitwidth::I8, VerificationResult::InapplicableRule), - // (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_ctz_8() { - test_aarch64_rule_with_lhs_termname_simple( - "ctz_8", - "ctz", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_ctz_16() { - test_aarch64_rule_with_lhs_termname_simple( - "ctz_16", - "ctz", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_ctz_32_64() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/ctz/broken_ctz.isle", - "clz".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_ctz_8() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/ctz/broken_ctz8.isle", - "ctz".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_ctz_16() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/ctz/broken_ctz16.isle", - "ctz".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_small_rotr() { - let config = Config { - term: "small_rotr".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, args, lhs, rhs| { - let ty_arg = *args.first().unwrap(); - let lower_8_bits_eq = { - let mask = smt.atom("#x00000000000000FF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - let lower_16_bits_eq = { - let mask = smt.atom("#x000000000000FFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - smt.ite( - smt.eq(ty_arg, smt.atom("8")), - lower_8_bits_eq, - lower_16_bits_eq, - ) - })), - names: Some(vec!["small_rotr".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(Bitwidth::I64, VerificationResult::Success)]); -} - -#[test] -fn test_broken_small_rotr_to_shifts() { - let config = Config { - term: "small_rotr".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, args, lhs, rhs| { - let ty_arg = *args.first().unwrap(); - let lower_8_bits_eq = { - let mask = smt.atom("#x00000000000000FF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - let lower_16_bits_eq = { - let mask = smt.atom("#x000000000000FFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - smt.ite( - smt.eq(ty_arg, smt.atom("8")), - lower_8_bits_eq, - lower_16_bits_eq, - ) - })), - names: None, - }; - test_from_file_with_config_simple( - "./examples/broken/broken_mask_small_rotr.isle", - config, - vec![( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - )], - ); -} - -#[test] -fn test_broken_small_rotr_to_shifts_2() { - let config = Config { - term: "small_rotr".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, args, lhs, rhs| { - let ty_arg = *args.first().unwrap(); - let lower_8_bits_eq = { - let mask = smt.atom("#x00000000000000FF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - let lower_16_bits_eq = { - let mask = smt.atom("#x000000000000FFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - smt.ite( - smt.eq(ty_arg, smt.atom("8")), - lower_8_bits_eq, - lower_16_bits_eq, - ) - })), - names: None, - }; - test_from_file_with_config_simple( - "./examples/broken/broken_rule_or_small_rotr.isle", - config, - vec![( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - )], - ); -} - -#[test] -fn test_named_small_rotr_imm() { - let config = Config { - term: "small_rotr_imm".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, args, lhs, rhs| { - let ty_arg = *args.first().unwrap(); - let lower_8_bits_eq = { - let mask = smt.atom("#x00000000000000FF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - let lower_16_bits_eq = { - let mask = smt.atom("#x000000000000FFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - smt.ite( - smt.eq(ty_arg, smt.atom("8")), - lower_8_bits_eq, - lower_16_bits_eq, - ) - })), - names: Some(vec!["small_rotr_imm".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(Bitwidth::I64, VerificationResult::Success)]); -} - -#[test] -fn test_named_rotl_fits_in_16() { - test_aarch64_rule_with_lhs_termname_simple( - "rotl_fits_in_16", - "rotl", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotl_32_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "rotl_32_base_case", - "rotl", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_32_general_rotl_to_rotr() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/broken_32_general_rotl_to_rotr.isle", - "rotl".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotl_64_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "rotl_64_base_case", - "rotl", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_broken_fits_in_16_rotl_to_rotr() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/broken_fits_in_16_rotl_to_rotr.isle", - "rotl".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotl_fits_in_16_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "rotl_fits_in_16_imm", - "rotl", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotl_64_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "rotl_64_imm", - "rotl", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_rotl_32_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "rotl_32_imm", - "rotl", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_fits_in_16_with_imm_rotl_to_rotr() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle", - "rotl".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotr_fits_in_16() { - test_aarch64_rule_with_lhs_termname_simple( - "rotr_fits_in_16", - "rotr", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotr_fits_in_16_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "rotr_fits_in_16_imm", - "rotr", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotr_32_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "rotr_32_base_case", - "rotr", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotr_32_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "rotr_32_imm", - "rotr", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_rotr_64_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "rotr_64_base_case", - "rotr", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_rotr_64_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "rotr_64_imm", - "rotr", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_band_fits_in_64() { - test_aarch64_rule_with_lhs_termname_simple( - "band_fits_in_64", - "band", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_broken_band_fits_in_32() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/broken_fits_in_32_band.isle", - "band".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_bor_fits_in_64() { - test_aarch64_rule_with_lhs_termname_simple( - "bor_fits_in_64", - "bor", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_broken_bor_fits_in_32() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/broken_fits_in_32_bor.isle", - "bor".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_bxor_fits_in_64() { - test_aarch64_rule_with_lhs_termname_simple( - "bxor_fits_in_64", - "bxor", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_band_not_right() { - test_aarch64_rule_with_lhs_termname_simple( - "band_not_right", - "band", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_band_not_left() { - test_aarch64_rule_with_lhs_termname_simple( - "band_not_left", - "band", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_bor_not_right() { - test_aarch64_rule_with_lhs_termname_simple( - "bor_not_right", - "bor", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_bor_not_left() { - test_aarch64_rule_with_lhs_termname_simple( - "bor_not_left", - "bor", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_bxor_not_right() { - test_aarch64_rule_with_lhs_termname_simple( - "bxor_not_right", - "bxor", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_bxor_not_left() { - test_aarch64_rule_with_lhs_termname_simple( - "bxor_not_left", - "bxor", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_bnot() { - test_aarch64_rule_with_lhs_termname_simple("bnot_base_case", "bnot", all_success_result()) -} - -#[test] -fn test_named_bnot_ishl() { - test_aarch64_rule_with_lhs_termname_simple("bnot_ishl", "bnot", all_success_result()) -} - -#[test] -fn test_named_ishl_64() { - test_aarch64_rule_with_lhs_termname_simple( - "ishl_64", - "ishl", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_ishl_64_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "ishl_64", - "ishl", - ConcreteTest { - termname: "ishl".to_string(), - args: vec![ - ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000001" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000010" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - ], - output: ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000100" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - }, - ) -} - -#[test] -fn test_named_ishl_fits_in_32() { - test_aarch64_rule_with_lhs_termname_simple( - "ishl_fits_in_32", - "ishl", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_ishl_fits_in_32_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "ishl_fits_in_32", - "ishl", - ConcreteTest { - termname: "ishl".to_string(), - args: vec![ - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000010".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ], - output: ConcreteInput { - literal: "#b00000100".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_named_sshr_64() { - test_aarch64_rule_with_lhs_termname_simple( - "sshr_64", - "sshr", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_sshr_fits_in_32() { - test_aarch64_rule_with_lhs_termname_simple( - "sshr_fits_in_32", - "sshr", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_sshr_fits_in_32_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "sshr_fits_in_32", - "sshr", - ConcreteTest { - termname: "sshr".to_string(), - args: vec![ - ConcreteInput { - literal: "#b10100000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ], - output: ConcreteInput { - literal: "#b11010000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_named_ushr_64() { - test_aarch64_rule_with_lhs_termname_simple( - "ushr_64", - "ushr", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_ushr_fits_in_32() { - test_aarch64_rule_with_lhs_termname_simple( - "ushr_fits_in_32", - "ushr", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_ushr_fits_in_32_concrete() { - test_concrete_aarch64_rule_with_lhs_termname( - "ushr_fits_in_32", - "ushr", - ConcreteTest { - termname: "ushr".to_string(), - args: vec![ - ConcreteInput { - literal: "#b10100000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ], - output: ConcreteInput { - literal: "#b01010000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_named_do_shift_64_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "do_shift_64_base_case", - "do_shift", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_do_shift_imm() { - let config = Config { - term: "do_shift".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, _args, lhs, rhs| { - let mask = smt.atom("#x00000000000000FF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - })), - names: Some(vec!["do_shift_imm".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(Bitwidth::I8, VerificationResult::Success)]); - let config = Config { - term: "do_shift".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, _args, lhs, rhs| { - let mask = smt.atom("#x000000000000FFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - })), - names: Some(vec!["do_shift_imm".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(Bitwidth::I16, VerificationResult::Success)]); - let config = Config { - term: "do_shift".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, _args, lhs, rhs| { - let mask = smt.atom("#x00000000FFFFFFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - })), - names: Some(vec!["do_shift_imm".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(Bitwidth::I32, VerificationResult::Success)]); - test_aarch64_rule_with_lhs_termname_simple( - "do_shift_imm", - "do_shift", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_do_shift_fits_in_16() { - let config = Config { - term: "do_shift".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, args, lhs, rhs| { - let ty_arg = args[1]; - let lower_8_bits_eq = { - let mask = smt.atom("#x00000000000000FF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - let lower_16_bits_eq = { - let mask = smt.atom("#x000000000000FFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - }; - smt.ite( - smt.eq(ty_arg, smt.atom("8")), - lower_8_bits_eq, - lower_16_bits_eq, - ) - })), - names: Some(vec!["do_shift_fits_in_16".to_string()]), - }; - test_aarch64_with_config_simple( - config, - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - ], - ); - - test_aarch64_rule_with_lhs_termname_simple( - "do_shift_fits_in_16", - "do_shift", - vec![ - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_do_shift_fits_in_16_concrete() { - // (decl do_shift (ALUOp Type Reg Value) Reg) - - test_concrete_aarch64_rule_with_lhs_termname( - "do_shift_fits_in_16", - "do_shift", - ConcreteTest { - termname: "do_shift".to_string(), - args: vec![ - ConcreteInput { - literal: "#x0e".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "16".to_string(), - ty: veri_ir::Type::Int, - }, - ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000001" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - ConcreteInput { - literal: "#b0000000000000001".to_string(), - ty: veri_ir::Type::BitVector(Some(16)), - }, - ], - output: ConcreteInput { - literal: "#b0000000000000000000000000000000000000000000000000000000000000010" - .to_string(), - ty: veri_ir::Type::BitVector(Some(64)), - }, - }, - ) -} - -#[test] -fn test_named_do_shift_32_base_case() { - test_aarch64_rule_with_lhs_termname_simple( - "do_shift_32_base_case", - "do_shift", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ); - let config = Config { - term: "do_shift".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, _args, lhs, rhs| { - let mask = smt.atom("#x00000000FFFFFFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - })), - names: Some(vec!["do_shift_32_base_case".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(Bitwidth::I32, VerificationResult::Success)]); -} - -#[test] -fn test_broken_do_shift_32() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/shifts/broken_do_shift_32.isle", - "do_shift".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ); - let config = Config { - term: "do_shift".to_string(), - distinct_check: true, - custom_assumptions: None, - custom_verification_condition: Some(Box::new(|smt, _args, lhs, rhs| { - let mask = smt.atom("#x00000000FFFFFFFF"); - smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) - })), - names: None, - }; - test_from_file_with_config_simple( - "./examples/broken/shifts/broken_do_shift_32.isle", - config, - vec![( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - )], - ); -} - -#[test] -fn test_broken_ishl_to_do_shift_64() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/shifts/broken_ishl_to_do_shift_64.isle", - "ishl".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_broken_sshr_to_do_shift_fits_in_32() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/shifts/broken_sshr_to_do_shift_fits_in_32.isle", - "sshr".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_broken_sshr_to_do_shift_fits_in_32_concrete() { - test_concrete_input_from_file_with_lhs_termname( - "./examples/broken/shifts/broken_sshr_to_do_shift_fits_in_32.isle", - "sshr".to_string(), - ConcreteTest { - termname: "sshr".to_string(), - args: vec![ - ConcreteInput { - literal: "#b10100000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ], - // Wrong output: - output: ConcreteInput { - literal: "#b01010000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_broken_ushr_to_do_shift_fits_in_32() { - test_from_file_with_lhs_termname_simple( - "./examples/broken/shifts/broken_ushr_to_do_shift_fits_in_32.isle", - "ushr".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_if_let() { - test_from_file_with_lhs_termname_simple( - "./examples/constructs/if-let.isle", - "iadd".to_string(), - all_success_result(), - ); -} - -#[test] -fn test_named_icmp_8_16_32_64() { - test_aarch64_rule_with_lhs_termname_simple( - "icmp_8_16_32_64", - "icmp", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_lower_icmp_into_reg_8_16_32_64() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_into_reg_8_16_32_64", - "lower_icmp_into_reg", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_lower_icmp_into_reg_8_16_32_64_concrete_1() { - test_concrete_aarch64_rule_with_lhs_termname( - "lower_icmp_into_reg_8_16_32_64", - "lower_icmp_into_reg", - ConcreteTest { - termname: "lower_icmp_into_reg".to_string(), - args: vec![ - ConcreteInput { - literal: "#b00000000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "8".to_string(), - ty: veri_ir::Type::Int, - }, - ConcreteInput { - literal: "8".to_string(), - ty: veri_ir::Type::Int, - }, - ], - output: ConcreteInput { - literal: "#b00000000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -#[test] -fn test_named_lower_icmp_into_reg_8_16_32_64_concrete_2() { - test_concrete_aarch64_rule_with_lhs_termname( - "lower_icmp_into_reg_8_16_32_64", - "lower_icmp_into_reg", - ConcreteTest { - termname: "lower_icmp_into_reg".to_string(), - args: vec![ - ConcreteInput { - literal: "#b00000000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "#b00000000".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - ConcreteInput { - literal: "8".to_string(), - ty: veri_ir::Type::Int, - }, - ConcreteInput { - literal: "8".to_string(), - ty: veri_ir::Type::Int, - }, - ], - output: ConcreteInput { - literal: "#b00000001".to_string(), - ty: veri_ir::Type::BitVector(Some(8)), - }, - }, - ) -} - -// Narrow types fail because of rule priorities -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_lower_icmp_32_64() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_32_64", - "lower_icmp", - vec![ - // (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - // ( - // Bitwidth::I16, - // VerificationResult::Failure(Counterexample {}), - // ), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_lower_icmp_8_16_signed() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_8_16_signed", - "lower_icmp", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -// TODO AVH: Currently fails because needs priorities to show this -// only applies to unsigned cond codes -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_lower_icmp_8_16_unsigned_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_8_16_unsigned_imm", - "lower_icmp", - vec![ - // (Bitwidth::I8, VerificationResult::Success), - // (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -// TODO AVH: Currently fails because needs priorities to show this -// only applies to unsigned cond codes -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_lower_icmp_8_16_unsigned() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_8_16_unsigned", - "lower_icmp", - vec![ - // (Bitwidth::I8, VerificationResult::Success), - // (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -// AVH TODO: this rule requires priorities to be correct for narrow cases -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_lower_icmp_32_64_const() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_32_64_const", - "lower_icmp", - vec![ - // (Bitwidth::I8, VerificationResult::InapplicableRule), - // (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_lower_icmp_const_32_64_imm() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_const_32_64_imm", - "lower_icmp_const", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -// AVH TODO: this rule requires priorities and a custom verification condition -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_lower_icmp_const_32_64_sgte() { - // Note: only one distinct condition code is matched on, so need to disable - // distinctness check - - let config = Config { - term: "lower_icmp_const".to_string(), - distinct_check: false, - custom_verification_condition: None, - custom_assumptions: None, - names: Some(vec!["lower_icmp_const_32_64_sgte".to_string()]), - }; - test_aarch64_with_config_simple( - config, - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - // Currently fails! The rewrite is not semantics-preserving - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -// AVH TODO: this rule requires priorities and a custom verification condition -// https://github.com/avanhatt/wasmtime/issues/32 -#[test] -fn test_named_lower_icmp_const_32_64_ugte() { - // Note: only one distinct condition code is matched on, so need to disable - // distinctness check - - let config = Config { - term: "lower_icmp_const".to_string(), - distinct_check: false, - custom_verification_condition: None, - custom_assumptions: None, - names: Some(vec!["lower_icmp_const_32_64_ugte".to_string()]), - }; - test_aarch64_with_config_simple( - config, - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - // Currently fails! The rewrite is not semantics-preserving - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ) -} - -#[test] -fn test_named_lower_icmp_const_32_64() { - test_aarch64_rule_with_lhs_termname_simple( - "lower_icmp_const_32_64", - "lower_icmp_const", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_umax() { - test_aarch64_rule_with_lhs_termname_simple( - "umax", - "umax", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_smax() { - test_aarch64_rule_with_lhs_termname_simple( - "smax", - "smax", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_umin() { - test_aarch64_rule_with_lhs_termname_simple( - "umin", - "umin", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_smin() { - test_aarch64_rule_with_lhs_termname_simple( - "smin", - "smin", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_iabs_64() { - test_aarch64_rule_with_lhs_termname_simple( - "iabs_64", - "iabs", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_iabs_8_16_32() { - test_aarch64_rule_with_lhs_termname_simple( - "iabs_8_16_32", - "iabs", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_bitselect() { - test_aarch64_rule_with_lhs_termname_simple("bitselect", "bitselect", all_success_result()) -} - -#[test] -fn test_named_iconst() { - test_aarch64_rule_with_lhs_termname_simple("iconst", "iconst", all_success_result()) -} - -// Can't currently verify because ConsumesFlags requires a non-functional -// interpretation -// #[test] -// fn test_named_cmp_and_choose_8_16() { -// -// let config = Config { -// dyn_width: false, -// term: "cmp_and_choose".to_string(), -// distinct_check: true, -// custom_verification_condition: Some(Box::new(|smt, args, lhs, rhs| { -// let ty_arg = *args.first().unwrap(); -// let lower_8_bits_eq = { -// let mask = smt.atom("#x00000000000000FF"); -// smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) -// }; -// let lower_16_bits_eq = { -// let mask = smt.atom("#x000000000000FFFF"); -// smt.eq(smt.bvand(mask, lhs), smt.bvand(mask, rhs)) -// }; -// smt.ite( -// smt.eq(ty_arg, smt.atom("8")), -// lower_8_bits_eq, -// lower_16_bits_eq, -// ) -// })), -// names: Some(vec!["cmp_and_choose_8_16".to_string()]), -// }; -// test_aarch64_with_config_simple( -// config, -// vec![ -// (Bitwidth::I8, VerificationResult::Failure(Counterexample { })), -// (Bitwidth::I16, VerificationResult::Failure(Counterexample { })), -// (Bitwidth::I32, VerificationResult::InapplicableRule), -// (Bitwidth::I64, VerificationResult::InapplicableRule), -// ], -// ); -// }) -// } - -#[test] -fn test_named_popcnt_8() { - test_aarch64_rule_with_lhs_termname_simple( - "popcnt_8", - "popcnt", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_popcnt_16() { - test_aarch64_rule_with_lhs_termname_simple( - "popcnt_16", - "popcnt", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_popcnt_32() { - test_aarch64_rule_with_lhs_termname_simple( - "popcnt_32", - "popcnt", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -// Currently too slow -// https://github.com/avanhatt/wasmtime/issues/36 -#[test] -fn test_named_popcnt_64() { - test_aarch64_rule_with_lhs_termname_simple( - "popcnt_64", - "popcnt", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - // (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -// Currently too slow -// https://github.com/avanhatt/wasmtime/issues/36 -#[test] -#[ignore] -fn test_named_slow_popcnt_64() { - test_aarch64_rule_with_lhs_termname_simple( - "popcnt_64", - "popcnt", - vec![(Bitwidth::I64, VerificationResult::Unknown)], - ) -} - -#[test] -fn test_named_operand_size_32() { - // Since there are no bitvectors in the signature, need a custom assumption - // hook to pass through the value of the type argument - - static EXPECTED: [(Bitwidth, VerificationResult); 4] = [ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ]; - for (ty, result) in &EXPECTED { - let config = Config { - term: "operand_size".to_string(), - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: Some(Box::new(|smt, args| { - let ty_arg = *args.first().unwrap(); - smt.eq(ty_arg, smt.numeral(*ty as usize)) - })), - names: Some(vec!["operand_size_32".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(*ty, result.clone())]); - } -} - -#[test] -fn test_named_operand_size_64() { - // Since there are no bitvectors in the signature, need a custom assumption - // hook to pass through the value of the type argument - - // Lower types precluded by priorities - static EXPECTED: [(Bitwidth, VerificationResult); 1] = [ - // (Bitwidth::I8, VerificationResult::Success), - // (Bitwidth::I16, VerificationResult::Success), - // (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ]; - for (ty, result) in &EXPECTED { - let config = Config { - term: "operand_size".to_string(), - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: Some(Box::new(|smt, args| { - let ty_arg = *args.first().unwrap(); - smt.eq(ty_arg, smt.numeral(*ty as usize)) - })), - names: Some(vec!["operand_size_64".to_string()]), - }; - test_aarch64_with_config_simple(config, vec![(*ty, result.clone())]); - } -} - -#[test] -fn test_named_output_reg() { - test_aarch64_rule_with_lhs_termname_simple( - "output_reg", - "output_reg", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_broken_imm_udiv_cve_underlying() { - // Since there are no bitvectors in the signature, need a custom assumption - // hook to pass through the value of the type argument - - static EXPECTED: [(Bitwidth, VerificationResult); 4] = [ - (Bitwidth::I8, VerificationResult::Failure(Counterexample {})), - ( - Bitwidth::I16, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - (Bitwidth::I64, VerificationResult::Success), - ]; - for (ty, result) in &EXPECTED { - let config = Config { - term: "imm".to_string(), - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: Some(Box::new(|smt, args| { - let ty_arg = *args.first().unwrap(); - smt.eq(ty_arg, smt.numeral(*ty as usize)) - })), - names: None, - }; - test_from_file_with_config_simple( - "./examples/broken/udiv/udiv_cve_underlying.isle", - config, - vec![(*ty, result.clone())], - ); - } -} - -#[test] -fn test_broken_imm_udiv_cve_underlying_32() { - // Since there are no bitvectors in the signature, need a custom assumption - // hook to pass through the value of the type argument - - static EXPECTED: [(Bitwidth, VerificationResult); 1] = [( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - )]; - for (ty, result) in &EXPECTED { - let config = Config { - term: "imm".to_string(), - distinct_check: true, - custom_verification_condition: None, - custom_assumptions: Some(Box::new(|smt, args| { - let ty_arg = *args.first().unwrap(); - smt.eq(ty_arg, smt.numeral(*ty as usize)) - })), - names: None, - }; - test_from_file_with_config_simple( - "./examples/broken/udiv/udiv_cve_underlying.isle", - config, - vec![(*ty, result.clone())], - ); - } -} - -// x64 - -#[test] -fn test_named_x64_iadd_base_case_32_or_64_lea() { - test_x64_rule_with_lhs_termname_simple( - "iadd_base_case_32_or_64_lea", - "iadd", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_x64_to_amode_add_base_case() { - test_x64_rule_with_lhs_termname_simple( - "to_amode_add_base_case", - "to_amode_add", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_to_amode_add_const_rhs() { - test_x64_rule_with_lhs_termname_simple( - "to_amode_add_const_rhs", - "to_amode_add", - vec![ - // TODO: make this work for I32 - // (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_x64_to_amode_add_const_lhs() { - test_x64_rule_with_lhs_termname_simple( - "to_amode_add_const_lhs", - "to_amode_add", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_to_amode_add_const_fold_iadd_lhs_rhs() { - test_x64_rule_with_lhs_termname_simple( - "to_amode_add_const_fold_iadd_lhs_rhs", - "to_amode_add", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_to_amode_add_const_fold_iadd_lhs_lhs() { - test_x64_rule_with_lhs_termname_simple( - "to_amode_add_const_fold_iadd_lhs_lhs", - "to_amode_add", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_to_amode_add_const_fold_iadd_rhs_rhs() { - test_x64_rule_with_lhs_termname_simple( - "to_amode_add_const_fold_iadd_rhs_rhs", - "to_amode_add", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_to_amode_add_const_fold_iadd_rhs_lhs() { - test_x64_rule_with_lhs_termname_simple( - "to_amode_add_const_fold_iadd_rhs_lhs", - "to_amode_add", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_amode_imm_reg_base() { - test_x64_rule_with_lhs_termname_simple( - "amode_imm_reg_base", - "amode_imm_reg", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_amode_imm_reg_iadd() { - test_x64_rule_with_lhs_termname_simple( - "amode_imm_reg_iadd", - "amode_imm_reg", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_amode_imm_reg_reg_shift_no_shift() { - test_x64_rule_with_lhs_termname_simple( - "amode_imm_reg_reg_shift_no_shift", - "amode_imm_reg_reg_shift", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_amode_imm_reg_reg_shift_shl_rhs() { - test_x64_rule_with_lhs_termname_simple( - "amode_imm_reg_reg_shift_shl_rhs", - "amode_imm_reg_reg_shift", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_x64_amode_imm_reg_reg_shift_shl_lhs() { - test_x64_rule_with_lhs_termname_simple( - "amode_imm_reg_reg_shift_shl_lhs", - "amode_imm_reg_reg_shift", - vec![(Bitwidth::I64, VerificationResult::Success)], - ) -} - -#[test] -fn test_named_load_i8_aarch64_uload8() { - test_aarch64_rule_with_lhs_termname_simple( - "load_i8_aarch64_uload8", - "load", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_load_i16_aarch64_uload16() { - test_aarch64_rule_with_lhs_termname_simple( - "load_i16_aarch64_uload16", - "load", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_load_i32_aarch64_uload32() { - test_aarch64_rule_with_lhs_termname_simple( - "load_i32_aarch64_uload32", - "load", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_load_i64_aarch64_uload64() { - test_aarch64_rule_with_lhs_termname_simple( - "load_i64_aarch64_uload64", - "load", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_store_i8_aarch64_store8() { - test_aarch64_rule_with_lhs_termname_simple( - "store_i8_aarch64_store8", - "store", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_store_i16_aarch64_store16() { - test_aarch64_rule_with_lhs_termname_simple( - "store_i16_aarch64_store16", - "store", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_store_i32_aarch64_store32() { - test_aarch64_rule_with_lhs_termname_simple( - "store_i32_aarch64_store32", - "store", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_store_i64_aarch64_store64() { - test_aarch64_rule_with_lhs_termname_simple( - "store_i64_aarch64_store64", - "store", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::InapplicableRule), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_named_load_sub64_x64_movzx() { - test_x64_rule_with_lhs_termname_simple( - "load_sub64_x64_movzx", - "load", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::InapplicableRule), - ], - ) -} - -#[test] -fn test_named_store_x64_add_mem() { - test_x64_rule_with_lhs_termname_simple( - "store_x64_add_mem", - "store", - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} -#[test] -fn test_named_store_x64_movrm() { - test_x64_rule_with_lhs_termname_simple( - "store_x64_movrm", - "store", - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ) -} - -#[test] -fn test_load_conditional() { - test_from_file_with_lhs_termname_simple( - "./examples/load/load_conditional.isle", - "lhs".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ); -} - -#[test] -fn test_store_switch() { - test_from_file_with_lhs_termname_simple( - "./examples/store/store_switch.isle", - "lhs".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::Success), - (Bitwidth::I16, VerificationResult::Success), - (Bitwidth::I32, VerificationResult::Success), - (Bitwidth::I64, VerificationResult::Success), - ], - ); -} - -#[test] -#[should_panic] -fn test_load_add_panic() { - test_from_file_with_lhs_termname_simple( - "./examples/load/load_add_panic.isle", - "lhs".to_string(), - all_failure_result(), - ); -} - -#[test] -fn test_broken_isub_store_with_load() { - test_from_file_with_lhs_termname_simple( - "./examples/store/broken_isub_store_with_load.isle", - "store".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ); -} - -#[test] -fn test_broken_bvsub_store_with_load() { - test_from_file_with_lhs_termname_simple( - "./examples/store/broken_bvsub_store_with_load.isle", - "store".to_string(), - vec![ - (Bitwidth::I8, VerificationResult::InapplicableRule), - (Bitwidth::I16, VerificationResult::InapplicableRule), - ( - Bitwidth::I32, - VerificationResult::Failure(Counterexample {}), - ), - ( - Bitwidth::I64, - VerificationResult::Failure(Counterexample {}), - ), - ], - ); -}