Bug 1547682: Bump Cranelift to cc216b46b35a797d03c0f3e8b16a2096f1c6db61; r=lth
authorBenjamin Bouvier <benj@benj.me>
Tue, 30 Apr 2019 14:22:41 +0200
changeset 532866 bc152a886c75ad2b0be20c3c380834fb1447f281
parent 532672 6f732caaed60783f57944a66f7ea494f5fd78d6c
child 532867 d3e245822b9fa18652219d8332ff2569fe40dca3
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1547682
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1547682: Bump Cranelift to cc216b46b35a797d03c0f3e8b16a2096f1c6db61; r=lth Differential Revision: https://phabricator.services.mozilla.com/D29343
.cargo/config.in
Cargo.lock
Cargo.toml
third_party/rust/cranelift-codegen-meta/.cargo-checksum.json
third_party/rust/cranelift-codegen-meta/src/cdsl/ast.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/cpu_modes.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/formats.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/inst.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/isa.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/mod.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/operands.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/settings.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/type_inference.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/typevar.rs
third_party/rust/cranelift-codegen-meta/src/cdsl/xform.rs
third_party/rust/cranelift-codegen-meta/src/gen_inst.rs
third_party/rust/cranelift-codegen-meta/src/gen_legalizer.rs
third_party/rust/cranelift-codegen-meta/src/gen_settings.rs
third_party/rust/cranelift-codegen-meta/src/isa/arm32/mod.rs
third_party/rust/cranelift-codegen-meta/src/isa/arm64/mod.rs
third_party/rust/cranelift-codegen-meta/src/isa/riscv/mod.rs
third_party/rust/cranelift-codegen-meta/src/isa/x86/legalize.rs
third_party/rust/cranelift-codegen-meta/src/isa/x86/mod.rs
third_party/rust/cranelift-codegen-meta/src/lib.rs
third_party/rust/cranelift-codegen-meta/src/shared/immediates.rs
third_party/rust/cranelift-codegen-meta/src/shared/legalize.rs
third_party/rust/cranelift-codegen-meta/src/shared/mod.rs
third_party/rust/cranelift-codegen-meta/src/srcgen.rs
third_party/rust/cranelift-codegen-meta/src/unique_table.rs
third_party/rust/cranelift-codegen/.cargo-checksum.json
third_party/rust/cranelift-codegen/meta-python/build.py
third_party/rust/cranelift-codegen/meta-python/cdsl/ast.py
third_party/rust/cranelift-codegen/meta-python/cdsl/settings.py
third_party/rust/cranelift-codegen/meta-python/gen_legalizer.py
third_party/rust/cranelift-codegen/meta-python/gen_settings.py
third_party/rust/cranelift-codegen/meta-python/isa/x86/encodings.py
third_party/rust/cranelift-codegen/meta-python/isa/x86/settings.py
third_party/rust/cranelift-codegen/meta-python/test_gen_legalizer.py
third_party/rust/cranelift-codegen/src/context.rs
third_party/rust/cranelift-codegen/src/divconst_magic_numbers.rs
third_party/rust/cranelift-codegen/src/ir/dfg.rs
third_party/rust/cranelift-codegen/src/ir/function.rs
third_party/rust/cranelift-codegen/src/ir/mod.rs
third_party/rust/cranelift-codegen/src/isa/constraints.rs
third_party/rust/cranelift-codegen/src/isa/x86/enc_tables.rs
third_party/rust/cranelift-codegen/src/legalizer/heap.rs
third_party/rust/cranelift-codegen/src/lib.rs
third_party/rust/cranelift-codegen/src/print_errors.rs
third_party/rust/cranelift-codegen/src/regalloc/context.rs
third_party/rust/cranelift-codegen/src/regalloc/liveness.rs
third_party/rust/cranelift-codegen/src/settings.rs
third_party/rust/cranelift-codegen/src/simple_preopt.rs
third_party/rust/cranelift-codegen/src/value_label.rs
third_party/rust/cranelift-codegen/src/write.rs
third_party/rust/cranelift-frontend/.cargo-checksum.json
third_party/rust/cranelift-frontend/src/frontend.rs
third_party/rust/cranelift-wasm/.cargo-checksum.json
third_party/rust/cranelift-wasm/src/code_translator.rs
third_party/rust/cranelift-wasm/src/environ/dummy.rs
third_party/rust/cranelift-wasm/src/environ/spec.rs
third_party/rust/cranelift-wasm/src/func_translator.rs
third_party/rust/cranelift-wasm/src/lib.rs
third_party/rust/cranelift-wasm/src/state.rs
third_party/rust/cranelift-wasm/src/translation_utils.rs
third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -19,13 +19,13 @@ replace-with = "vendored-sources"
 
 [source."https://github.com/rust-lang-nursery/packed_simd"]
 git = "https://github.com/hsivonen/packed_simd"
 branch = "rust_1_32"
 replace-with = "vendored-sources"
 
 [source."https://github.com/CraneStation/Cranelift"]
 git = "https://github.com/CraneStation/Cranelift"
-rev = "538a0662bf90a1daa9921c10f34827ace134abf1"
+rev = "cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
 replace-with = "vendored-sources"
 
 [source.vendored-sources]
 directory = '@top_srcdir@/third_party/rust'
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -161,18 +161,18 @@ dependencies = [
  "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "baldrdash"
 version = "0.1.0"
 dependencies = [
  "bindgen 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
- "cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
+ "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
+ "cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "base64"
 version = "0.9.3"
@@ -582,67 +582,67 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-bforest"
 version = "0.30.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
-dependencies = [
- "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
+source = "git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61#cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
+dependencies = [
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
 ]
 
 [[package]]
 name = "cranelift-codegen"
 version = "0.30.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
-dependencies = [
- "cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
- "cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
- "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
+source = "git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61#cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
+dependencies = [
+ "cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
+ "cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-codegen-meta"
 version = "0.30.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
-dependencies = [
- "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
+source = "git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61#cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
+dependencies = [
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
 ]
 
 [[package]]
 name = "cranelift-entity"
 version = "0.30.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
+source = "git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61#cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
 
 [[package]]
 name = "cranelift-frontend"
 version = "0.30.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
-dependencies = [
- "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
+source = "git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61#cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
+dependencies = [
+ "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-wasm"
 version = "0.30.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
+source = "git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61#cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
 dependencies = [
  "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
- "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
- "cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
+ "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
+ "cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crc"
@@ -3654,22 +3654,22 @@ dependencies = [
 "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
 "checksum cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1465f8134efa296b4c19db34d909637cb2bf0f7aaf21299e23e18fa29ac557cf"
 "checksum core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4e2640d6d0bf22e82bed1b73c6aef8d5dd31e5abe6666c57e6d45e2649f4f887"
 "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
 "checksum core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "62ceafe1622ffc9a332199096841d0ff9912ec8cf8f9cde01e254a7d5217cd10"
 "checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae"
 "checksum cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72fa26cb151d3ae4b70f63d67d0fed57ce04220feafafbae7f503bef7aae590d"
 "checksum cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49726015ab0ca765144fcca61e4a7a543a16b795a777fa53f554da2fffff9a94"
-"checksum cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
-"checksum cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
-"checksum cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
-"checksum cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
-"checksum cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
-"checksum cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
+"checksum cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)" = "<none>"
+"checksum cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)" = "<none>"
+"checksum cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)" = "<none>"
+"checksum cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)" = "<none>"
+"checksum cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)" = "<none>"
+"checksum cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=cc216b46b35a797d03c0f3e8b16a2096f1c6db61)" = "<none>"
 "checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
 "checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
 "checksum crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2af0e75710d6181e234c8ecc79f14a97907850a541b13b0be1dd10992f2e4620"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
 "checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b"
 "checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a"
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -59,13 +59,13 @@ codegen-units = 1
 [patch.crates-io]
 libudev-sys = { path = "dom/webauthn/libudev-sys" }
 serde_derive = { git = "https://github.com/servo/serde", branch = "deserialize_from_enums10" }
 winapi = { git = "https://github.com/froydnj/winapi-rs", branch = "aarch64" }
 packed_simd = { git = "https://github.com/hsivonen/packed_simd", branch = "rust_1_32" }
 
 [patch.crates-io.cranelift-codegen]
 git = "https://github.com/CraneStation/Cranelift"
-rev = "538a0662bf90a1daa9921c10f34827ace134abf1"
+rev = "cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
 
 [patch.crates-io.cranelift-wasm]
 git = "https://github.com/CraneStation/Cranelift"
-rev = "538a0662bf90a1daa9921c10f34827ace134abf1"
+rev = "cc216b46b35a797d03c0f3e8b16a2096f1c6db61"
--- a/third_party/rust/cranelift-codegen-meta/.cargo-checksum.json
+++ b/third_party/rust/cranelift-codegen-meta/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"253c80832ab598570d604ae8a8108ea9835b8eef8d9b9645408ed025ce3b574a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/formats.rs":"98ab61698ad4c1fb81541b1820bd1c1561810bdcff2796dec541c98b4b1901d7","src/cdsl/inst.rs":"d5130c1a36a4e33d1374f9867119c3f2d79c384f12afc12e7b7b4518cf1f74b3","src/cdsl/isa.rs":"dd52d35fa963494b7da892a4a04a4f9978079bb2d86c6af4273a8dfdb82bdf51","src/cdsl/mod.rs":"2d2e216f8c3a81978a5113213559a5ab659bc112b6194cbe08a752313aad7f46","src/cdsl/operands.rs":"cc579fd543e36cf8e82938db331c145b77e29855ee2aa8c5dd949678f980796d","src/cdsl/regs.rs":"b99f24c3ecb46691625dc177b4e18d53e02265bc85a2f827a8d18381fe8f39bb","src/cdsl/settings.rs":"4ddeadf1542cc2ddec0f9e6c22d1637050da519586cd9fec0243c3eab9619f82","src/cdsl/type_inference.rs":"8aedb2e99dee299abbc327ce3a604d48f161580776225d2438a54bbec5b725fe","src/cdsl/types.rs":"4cc1f20eb8383fdee6a9e7ca0f7758e563a4fb715056b5edbd4db72f8dfd471b","src/cdsl/typevar.rs":"605786e2bf367879da500327fc003a4d2a663259c2dee76c87e5b99b6f6331ee","src/constant_hash.rs":"b8acd3f8712a4999819d9d9beced2938d9940a5748ba016c182f1132d97eefab","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_inst.rs":"795d30588a5e87e69f3510606c9aee1b8670d5aee5f3d54067f6c7508a67e565","src/gen_registers.rs":"a544a2b91fafe08639e39e50bea0892fda89fe2f6eaf111b2d5f3e98e4d07b86","src/gen_settings.rs":"77ee330b85a255c49247222f4d071da839b0520eddd3dc561867f7ae84e199ac","src/gen_types.rs":"3935da6c6a53f9332e06f74bc3a46270656b4d4231ad28ed2648d7b1d2774e90","src/isa/arm32/mod.rs":"6ed3be790b28d3115421be282a06b8c376295e1776c4b77243443799015ab70d","src/isa/arm64/mod.rs":"5c46082f68c958e83ffc636de893e2ff49fd8ce21ef357f359837ca48a60eaa5","src/isa/mod.rs":"fce60d19dd3c099ebee3ac5ae64a2bee363f13da9ff5a4960d3c1a0bee71d29a","src/isa/riscv/mod.rs":"785f0da2b04458793cb2d493c5e1eeb7ea339bc721df76dda69db3b49bcdfd27","src/isa/x86/instructions.rs":"bd6b02ccc79984ed4a5615ae3b20a60a4da3777495b72f771762a886f87d2335","src/isa/x86/mod.rs":"ba7c11aedb190f58432226a6dec8a125b385cc18fd2f70c46703d077904a3112","src/lib.rs":"99aec646c7b756c01544a181e9b56ba14fccfb2bce205a8c1f63fb31905630ca","src/shared/entities.rs":"e7a44d5f621d726479c3812384e78dd25e8c063d074c64d0908b3667e7d28af1","src/shared/formats.rs":"20908b1048c5e71a185de6b6ded79cdff2c26ddb38ba7b134b7a27f37e8324f3","src/shared/immediates.rs":"1e64836f82045d05da7c151e60cf1e66666af3e0c19179de3f37e72dc81e1bbd","src/shared/instructions.rs":"2a0993279b3529b2c31aa8e83589636104a005351463ec2d3b81b5ffe569d276","src/shared/mod.rs":"696c166d3c19bd84604583a7b8d7ec4f6671622ed581bfce8bee375d02067cbe","src/shared/settings.rs":"bad2dc0e1d71ee6fec6418aa79234296aa918e499a1671c3e5c1d4b0d84b6f49","src/shared/types.rs":"158d73840185e6aa8385463bbf6568efdda0c8de8284cf6b4e565f425ec5d921","src/srcgen.rs":"ad39143ae50f3b19f18a43131bdd3308852c70a9e532cc99f97624e7380b00d8","src/unique_table.rs":"bec9d48ee040216a7c9deab6d2c5050d7ce70e38482cc8957105fd7cbca3c33a"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"253c80832ab598570d604ae8a8108ea9835b8eef8d9b9645408ed025ce3b574a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/ast.rs":"3406fec29fe80c979dc938a0fddda354ed96611276068b951e2b5b23ad018338","src/cdsl/cpu_modes.rs":"57c40621115a58faa7af1c729ecaf8cda01c2b143008bde6d8c70885e7c3fd75","src/cdsl/formats.rs":"858f0a6ea62580a2812a8f2ff68dd25298b6ea4c2413657c41630a044492ea0d","src/cdsl/inst.rs":"ed31c12876ab384d0ddd2f6395a64bf3a2f847a4864147b4bb52308cdeef4980","src/cdsl/isa.rs":"201e57e580defead2d1eac82762d629de3882a6bc1c53a9726025a1eef7fd752","src/cdsl/mod.rs":"9345537d57736a00e2f47aeaab50107c481edeccaa873b2c157b0a05d02e3124","src/cdsl/operands.rs":"5217258f2c4911b5be29ca020e9f7460b85182a74d485e027a3bd8336fbf2891","src/cdsl/regs.rs":"b99f24c3ecb46691625dc177b4e18d53e02265bc85a2f827a8d18381fe8f39bb","src/cdsl/settings.rs":"885ea67d5d8346caa4937a4693d59d8eadf72c414d86b316f58712c0d3c9778b","src/cdsl/type_inference.rs":"2771631701c150e077c5dcf705c8ae8705944d86ab945ae9e7adc82f3ca5447a","src/cdsl/types.rs":"4cc1f20eb8383fdee6a9e7ca0f7758e563a4fb715056b5edbd4db72f8dfd471b","src/cdsl/typevar.rs":"bd63d403e1ab12130c484100e5bc54b32120f27163a38cee3f546f56b9819325","src/cdsl/xform.rs":"87aeb183ef4282e05c442046df4028d771be63acc3e0250def17cdca7a953477","src/constant_hash.rs":"b8acd3f8712a4999819d9d9beced2938d9940a5748ba016c182f1132d97eefab","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_inst.rs":"971104519426da5eebeae2118439e2f7fe011ac0ee54f470f6311a12ae1045b9","src/gen_legalizer.rs":"268e9922f2742c611cace9763d3904f9c113f6869ee9588ed3d19b53f6d57d56","src/gen_registers.rs":"a544a2b91fafe08639e39e50bea0892fda89fe2f6eaf111b2d5f3e98e4d07b86","src/gen_settings.rs":"4469bf496f9539835fce3cd9743ac0fbc270b3029b0e6c949f406e790685199c","src/gen_types.rs":"3935da6c6a53f9332e06f74bc3a46270656b4d4231ad28ed2648d7b1d2774e90","src/isa/arm32/mod.rs":"39c168a2fc979ee1ccaddf303d590f6b50019f1a0733a426c81e9bc5d57ee90c","src/isa/arm64/mod.rs":"335e238ff1a61026c88c11b55585030960c3938cccf901ffb7d1ce3c0fa6db41","src/isa/mod.rs":"fce60d19dd3c099ebee3ac5ae64a2bee363f13da9ff5a4960d3c1a0bee71d29a","src/isa/riscv/mod.rs":"589d6517b3780c3d08379e9f6c1f46d3bfbb754b9b0a455ec041780838f01d6d","src/isa/x86/instructions.rs":"bd6b02ccc79984ed4a5615ae3b20a60a4da3777495b72f771762a886f87d2335","src/isa/x86/legalize.rs":"67316297edffbb2bdf0b19ff353a634f1f609a60bd2858410c9140ad0d7ca2bf","src/isa/x86/mod.rs":"cbc037fef238b239b7244a6e766d14dca23999161645c8e2ef91dd7e0a131c67","src/lib.rs":"8c9364c6fce73c158abfb7c88ecff01dc608a05c250e89df9bec3082773cde6d","src/shared/entities.rs":"e7a44d5f621d726479c3812384e78dd25e8c063d074c64d0908b3667e7d28af1","src/shared/formats.rs":"20908b1048c5e71a185de6b6ded79cdff2c26ddb38ba7b134b7a27f37e8324f3","src/shared/immediates.rs":"ac3653ce7f83372833136d28e1809d23b1dc65e7de29ffa26b5e381fcb94d25b","src/shared/instructions.rs":"2a0993279b3529b2c31aa8e83589636104a005351463ec2d3b81b5ffe569d276","src/shared/legalize.rs":"6fc4bebca916bf68b3289f4750ed11148365181b04d497d1d1c1913db7a6fe19","src/shared/mod.rs":"7029cb0c5f7ad59cb2a7ea5f8b91b0fbb363d0704df208dcf8c069da6bfa4c13","src/shared/settings.rs":"bad2dc0e1d71ee6fec6418aa79234296aa918e499a1671c3e5c1d4b0d84b6f49","src/shared/types.rs":"158d73840185e6aa8385463bbf6568efdda0c8de8284cf6b4e565f425ec5d921","src/srcgen.rs":"79fee2f603b33f76f7c9c8b9452c745a363d732c40c0814d84001ff3ef805677","src/unique_table.rs":"90b7203b29241a1ede70f0a3e50d96799e0b41d8f7455170d6ffb127f87f3cc3"},"package":null}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/ast.rs
@@ -0,0 +1,653 @@
+use crate::cdsl::formats::FormatRegistry;
+use crate::cdsl::inst::{BoundInstruction, Instruction, InstructionPredicate};
+use crate::cdsl::operands::{OperandKind, OperandKindFields};
+use crate::cdsl::types::{LaneType, ValueType};
+use crate::cdsl::typevar::{TypeSetBuilder, TypeVar};
+
+use cranelift_entity::{entity_impl, PrimaryMap};
+
+use std::fmt;
+
+pub enum Expr {
+    Var(VarIndex),
+    Literal(Literal),
+    Apply(Apply),
+}
+
+impl Expr {
+    pub fn maybe_literal(&self) -> Option<&Literal> {
+        match &self {
+            Expr::Literal(lit) => Some(lit),
+            _ => None,
+        }
+    }
+
+    pub fn maybe_var(&self) -> Option<VarIndex> {
+        if let Expr::Var(var) = &self {
+            Some(*var)
+        } else {
+            None
+        }
+    }
+
+    pub fn unwrap_var(&self) -> VarIndex {
+        self.maybe_var()
+            .expect("tried to unwrap a non-Var content in Expr::unwrap_var")
+    }
+
+    pub fn to_rust_code(&self, var_pool: &VarPool) -> String {
+        match self {
+            Expr::Var(var_index) => var_pool.get(*var_index).to_rust_code(),
+            Expr::Literal(literal) => literal.to_rust_code(),
+            Expr::Apply(a) => a.to_rust_code(var_pool),
+        }
+    }
+}
+
+/// An AST definition associates a set of variables with the values produced by an expression.
+pub struct Def {
+    pub apply: Apply,
+    pub defined_vars: Vec<VarIndex>,
+}
+
+impl Def {
+    pub fn to_comment_string(&self, var_pool: &VarPool) -> String {
+        let results = self
+            .defined_vars
+            .iter()
+            .map(|&x| var_pool.get(x).name)
+            .collect::<Vec<_>>();
+
+        let results = if results.len() == 1 {
+            results[0].to_string()
+        } else {
+            format!("({})", results.join(", "))
+        };
+
+        format!("{} << {}", results, self.apply.to_comment_string(var_pool))
+    }
+}
+
+pub struct DefPool {
+    pool: PrimaryMap<DefIndex, Def>,
+}
+
+impl DefPool {
+    pub fn new() -> Self {
+        Self {
+            pool: PrimaryMap::new(),
+        }
+    }
+    pub fn get(&self, index: DefIndex) -> &Def {
+        self.pool.get(index).unwrap()
+    }
+    pub fn get_mut(&mut self, index: DefIndex) -> &mut Def {
+        self.pool.get_mut(index).unwrap()
+    }
+    pub fn next_index(&self) -> DefIndex {
+        self.pool.next_key()
+    }
+    pub fn create(&mut self, apply: Apply, defined_vars: Vec<VarIndex>) -> DefIndex {
+        self.pool.push(Def {
+            apply,
+            defined_vars,
+        })
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct DefIndex(u32);
+entity_impl!(DefIndex);
+
+#[derive(Debug, Clone)]
+enum LiteralValue {
+    /// A value of an enumerated immediate operand.
+    ///
+    /// Some immediate operand kinds like `intcc` and `floatcc` have an enumerated range of values
+    /// corresponding to a Rust enum type. An `Enumerator` object is an AST leaf node representing one
+    /// of the values.
+    Enumerator(&'static str),
+
+    /// A bitwise value of an immediate operand, used for bitwise exact floating point constants.
+    Bits(u64),
+
+    /// A value of an integer immediate operand.
+    Int(i64),
+}
+
+#[derive(Clone)]
+pub struct Literal {
+    kind: OperandKind,
+    value: LiteralValue,
+}
+
+impl fmt::Debug for Literal {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(
+            fmt,
+            "Literal(kind={}, value={:?})",
+            self.kind.name, self.value
+        )
+    }
+}
+
+impl Literal {
+    pub fn enumerator_for(kind: &OperandKind, value: &'static str) -> Self {
+        if let OperandKindFields::ImmEnum(values) = &kind.fields {
+            assert!(
+                values.get(value).is_some(),
+                format!(
+                    "nonexistent value '{}' in enumeration '{}'",
+                    value, kind.name
+                )
+            );
+        } else {
+            panic!("enumerator is for enum values");
+        }
+        Self {
+            kind: kind.clone(),
+            value: LiteralValue::Enumerator(value),
+        }
+    }
+
+    pub fn bits(kind: &OperandKind, bits: u64) -> Self {
+        match kind.fields {
+            OperandKindFields::ImmValue => {}
+            _ => panic!("bits_of is for immediate scalar types"),
+        }
+        Self {
+            kind: kind.clone(),
+            value: LiteralValue::Bits(bits),
+        }
+    }
+
+    pub fn constant(kind: &OperandKind, value: i64) -> Self {
+        match kind.fields {
+            OperandKindFields::ImmValue => {}
+            _ => panic!("bits_of is for immediate scalar types"),
+        }
+        Self {
+            kind: kind.clone(),
+            value: LiteralValue::Int(value),
+        }
+    }
+
+    pub fn to_rust_code(&self) -> String {
+        let maybe_values = match &self.kind.fields {
+            OperandKindFields::ImmEnum(values) => Some(values),
+            OperandKindFields::ImmValue => None,
+            _ => panic!("impossible per construction"),
+        };
+
+        match self.value {
+            LiteralValue::Enumerator(value) => {
+                format!("{}::{}", self.kind.rust_type, maybe_values.unwrap()[value])
+            }
+            LiteralValue::Bits(bits) => format!("{}::with_bits({:#x})", self.kind.rust_type, bits),
+            LiteralValue::Int(val) => val.to_string(),
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum PatternPosition {
+    Source,
+    Destination,
+}
+
+/// A free variable.
+///
+/// When variables are used in `XForms` with source and destination patterns, they are classified
+/// as follows:
+///
+/// Input values: Uses in the source pattern with no preceding def. These may appear as inputs in
+/// the destination pattern too, but no new inputs can be introduced.
+///
+/// Output values: Variables that are defined in both the source and destination pattern.  These
+/// values may have uses outside the source pattern, and the destination pattern must compute the
+/// same value.
+///
+/// Intermediate values: Values that are defined in the source pattern, but not in the destination
+/// pattern. These may have uses outside the source pattern, so the defining instruction can't be
+/// deleted immediately.
+///
+/// Temporary values are defined only in the destination pattern.
+pub struct Var {
+    pub name: &'static str,
+
+    /// The `Def` defining this variable in a source pattern.
+    pub src_def: Option<DefIndex>,
+
+    /// The `Def` defining this variable in a destination pattern.
+    pub dst_def: Option<DefIndex>,
+
+    /// TypeVar representing the type of this variable.
+    type_var: Option<TypeVar>,
+
+    /// Is this the original type variable, or has it be redefined with set_typevar?
+    is_original_type_var: bool,
+}
+
+impl Var {
+    fn new(name: &'static str) -> Self {
+        Self {
+            name,
+            src_def: None,
+            dst_def: None,
+            type_var: None,
+            is_original_type_var: false,
+        }
+    }
+
+    /// Is this an input value to the src pattern?
+    pub fn is_input(&self) -> bool {
+        self.src_def.is_none() && self.dst_def.is_none()
+    }
+
+    /// Is this an output value, defined in both src and dst patterns?
+    pub fn is_output(&self) -> bool {
+        self.src_def.is_some() && self.dst_def.is_some()
+    }
+
+    /// Is this an intermediate value, defined only in the src pattern?
+    pub fn is_intermediate(&self) -> bool {
+        self.src_def.is_some() && self.dst_def.is_none()
+    }
+
+    /// Is this a temp value, defined only in the dst pattern?
+    pub fn is_temp(&self) -> bool {
+        self.src_def.is_none() && self.dst_def.is_some()
+    }
+
+    /// Get the def of this variable according to the position.
+    pub fn get_def(&self, position: PatternPosition) -> Option<DefIndex> {
+        match position {
+            PatternPosition::Source => self.src_def,
+            PatternPosition::Destination => self.dst_def,
+        }
+    }
+
+    pub fn set_def(&mut self, position: PatternPosition, def: DefIndex) {
+        assert!(
+            self.get_def(position).is_none(),
+            format!("redefinition of variable {}", self.name)
+        );
+        match position {
+            PatternPosition::Source => {
+                self.src_def = Some(def);
+            }
+            PatternPosition::Destination => {
+                self.dst_def = Some(def);
+            }
+        }
+    }
+
+    /// Get the type variable representing the type of this variable.
+    pub fn get_or_create_typevar(&mut self) -> TypeVar {
+        match &self.type_var {
+            Some(tv) => tv.clone(),
+            None => {
+                // Create a new type var in which we allow all types.
+                let tv = TypeVar::new(
+                    format!("typeof_{}", self.name),
+                    format!("Type of the pattern variable {:?}", self),
+                    TypeSetBuilder::all(),
+                );
+                self.type_var = Some(tv.clone());
+                self.is_original_type_var = true;
+                tv
+            }
+        }
+    }
+    pub fn get_typevar(&self) -> Option<TypeVar> {
+        self.type_var.clone()
+    }
+    pub fn set_typevar(&mut self, tv: TypeVar) {
+        self.is_original_type_var = if let Some(previous_tv) = &self.type_var {
+            *previous_tv == tv
+        } else {
+            false
+        };
+        self.type_var = Some(tv);
+    }
+
+    /// Check if this variable has a free type variable. If not, the type of this variable is
+    /// computed from the type of another variable.
+    pub fn has_free_typevar(&self) -> bool {
+        match &self.type_var {
+            Some(tv) => tv.base.is_none() && self.is_original_type_var,
+            None => false,
+        }
+    }
+
+    pub fn to_rust_code(&self) -> String {
+        self.name.into()
+    }
+    fn rust_type(&self) -> String {
+        self.type_var.as_ref().unwrap().to_rust_code()
+    }
+}
+
+impl fmt::Debug for Var {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        fmt.write_fmt(format_args!(
+            "Var({}{}{})",
+            self.name,
+            if self.src_def.is_some() { ", src" } else { "" },
+            if self.dst_def.is_some() { ", dst" } else { "" }
+        ))
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct VarIndex(u32);
+entity_impl!(VarIndex);
+
+pub struct VarPool {
+    pool: PrimaryMap<VarIndex, Var>,
+}
+
+impl VarPool {
+    pub fn new() -> Self {
+        Self {
+            pool: PrimaryMap::new(),
+        }
+    }
+    pub fn get(&self, index: VarIndex) -> &Var {
+        self.pool.get(index).unwrap()
+    }
+    pub fn get_mut(&mut self, index: VarIndex) -> &mut Var {
+        self.pool.get_mut(index).unwrap()
+    }
+    pub fn create(&mut self, name: &'static str) -> VarIndex {
+        self.pool.push(Var::new(name))
+    }
+}
+
+pub enum ApplyTarget {
+    Inst(Instruction),
+    Bound(BoundInstruction),
+}
+
+impl ApplyTarget {
+    pub fn inst(&self) -> &Instruction {
+        match &self {
+            ApplyTarget::Inst(inst) => inst,
+            ApplyTarget::Bound(bound_inst) => &bound_inst.inst,
+        }
+    }
+}
+
+impl Into<ApplyTarget> for &Instruction {
+    fn into(self) -> ApplyTarget {
+        ApplyTarget::Inst(self.clone())
+    }
+}
+
+impl Into<ApplyTarget> for BoundInstruction {
+    fn into(self) -> ApplyTarget {
+        ApplyTarget::Bound(self)
+    }
+}
+
+pub fn bind(target: impl Into<ApplyTarget>, lane_type: impl Into<LaneType>) -> BoundInstruction {
+    let value_type = ValueType::from(lane_type.into());
+
+    let (inst, value_types) = match target.into() {
+        ApplyTarget::Inst(inst) => (inst, vec![value_type]),
+        ApplyTarget::Bound(bound_inst) => {
+            let mut new_value_types = bound_inst.value_types;
+            new_value_types.push(value_type);
+            (bound_inst.inst, new_value_types)
+        }
+    };
+
+    match &inst.polymorphic_info {
+        Some(poly) => {
+            assert!(
+                value_types.len() <= 1 + poly.other_typevars.len(),
+                format!("trying to bind too many types for {}", inst.name)
+            );
+        }
+        None => {
+            panic!(format!(
+                "trying to bind a type for {} which is not a polymorphic instruction",
+                inst.name
+            ));
+        }
+    }
+
+    BoundInstruction { inst, value_types }
+}
+
+/// Apply an instruction to arguments.
+///
+/// An `Apply` AST expression is created by using function call syntax on instructions. This
+/// applies to both bound and unbound polymorphic instructions.
+pub struct Apply {
+    pub inst: Instruction,
+    pub args: Vec<Expr>,
+    pub value_types: Vec<ValueType>,
+}
+
+impl Apply {
+    pub fn new(target: ApplyTarget, args: Vec<Expr>) -> Self {
+        let (inst, value_types) = match target.into() {
+            ApplyTarget::Inst(inst) => (inst, Vec::new()),
+            ApplyTarget::Bound(bound_inst) => (bound_inst.inst, bound_inst.value_types),
+        };
+
+        // Basic check on number of arguments.
+        assert!(
+            inst.operands_in.len() == args.len(),
+            format!("incorrect number of arguments in instruction {}", inst.name)
+        );
+
+        // Check that the kinds of Literals arguments match the expected operand.
+        for &imm_index in &inst.imm_opnums {
+            let arg = &args[imm_index];
+            if let Some(literal) = arg.maybe_literal() {
+                let op = &inst.operands_in[imm_index];
+                assert!(
+                    op.kind.name == literal.kind.name,
+                    format!(
+                        "Passing literal of kind {} to field of wrong kind {}",
+                        literal.kind.name, op.kind.name
+                    )
+                );
+            }
+        }
+
+        Self {
+            inst,
+            args,
+            value_types,
+        }
+    }
+
+    fn to_comment_string(&self, var_pool: &VarPool) -> String {
+        let args = self
+            .args
+            .iter()
+            .map(|arg| arg.to_rust_code(var_pool))
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        let mut inst_and_bound_types = vec![self.inst.name.to_string()];
+        inst_and_bound_types.extend(self.value_types.iter().map(|vt| vt.to_string()));
+        let inst_name = inst_and_bound_types.join(".");
+
+        format!("{}({})", inst_name, args)
+    }
+
+    fn to_rust_code(&self, var_pool: &VarPool) -> String {
+        let args = self
+            .args
+            .iter()
+            .map(|arg| arg.to_rust_code(var_pool))
+            .collect::<Vec<_>>()
+            .join(", ");
+        format!("{}({})", self.inst.name, args)
+    }
+
+    fn inst_predicate(
+        &self,
+        format_registry: &FormatRegistry,
+        var_pool: &VarPool,
+    ) -> InstructionPredicate {
+        let iform = format_registry.get(self.inst.format);
+
+        let mut pred = InstructionPredicate::new();
+        for (format_field, &op_num) in iform.imm_fields.iter().zip(self.inst.imm_opnums.iter()) {
+            let arg = &self.args[op_num];
+            if arg.maybe_var().is_some() {
+                // Ignore free variables for now.
+                continue;
+            }
+            pred = pred.and(InstructionPredicate::new_is_field_equal(
+                &format_field,
+                arg.to_rust_code(var_pool),
+            ));
+        }
+
+        // Add checks for any bound secondary type variables.  We can't check the controlling type
+        // variable this way since it may not appear as the type of an operand.
+        if self.value_types.len() > 1 {
+            let poly = self
+                .inst
+                .polymorphic_info
+                .as_ref()
+                .expect("must have polymorphic info if it has bounded types");
+            for (bound_type, type_var) in
+                self.value_types[1..].iter().zip(poly.other_typevars.iter())
+            {
+                pred = pred.and(InstructionPredicate::new_typevar_check(
+                    &self.inst, type_var, bound_type,
+                ));
+            }
+        }
+
+        pred
+    }
+
+    /// Same as `inst_predicate()`, but also check the controlling type variable.
+    pub fn inst_predicate_with_ctrl_typevar(
+        &self,
+        format_registry: &FormatRegistry,
+        var_pool: &VarPool,
+    ) -> InstructionPredicate {
+        let mut pred = self.inst_predicate(format_registry, var_pool);
+
+        if !self.value_types.is_empty() {
+            let bound_type = &self.value_types[0];
+            let poly = self.inst.polymorphic_info.as_ref().unwrap();
+            let type_check = if poly.use_typevar_operand {
+                InstructionPredicate::new_typevar_check(&self.inst, &poly.ctrl_typevar, bound_type)
+            } else {
+                InstructionPredicate::new_ctrl_typevar_check(&bound_type)
+            };
+            pred = pred.and(type_check);
+        }
+
+        pred
+    }
+
+    pub fn rust_builder(&self, defined_vars: &Vec<VarIndex>, var_pool: &VarPool) -> String {
+        let mut args = self
+            .args
+            .iter()
+            .map(|expr| expr.to_rust_code(var_pool))
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        // Do we need to pass an explicit type argument?
+        if let Some(poly) = &self.inst.polymorphic_info {
+            if !poly.use_typevar_operand {
+                args = format!("{}, {}", var_pool.get(defined_vars[0]).rust_type(), args);
+            }
+        }
+
+        format!("{}({})", self.inst.snake_name(), args)
+    }
+}
+
+// Simple helpers for legalize actions construction.
+
+pub enum DummyExpr {
+    Var(DummyVar),
+    Literal(Literal),
+    Apply(ApplyTarget, Vec<DummyExpr>),
+}
+
+#[derive(Clone)]
+pub struct DummyVar {
+    pub name: &'static str,
+}
+
+impl Into<DummyExpr> for DummyVar {
+    fn into(self) -> DummyExpr {
+        DummyExpr::Var(self)
+    }
+}
+impl Into<DummyExpr> for Literal {
+    fn into(self) -> DummyExpr {
+        DummyExpr::Literal(self)
+    }
+}
+
+pub fn var(name: &'static str) -> DummyVar {
+    DummyVar { name }
+}
+
+pub struct DummyDef {
+    pub expr: DummyExpr,
+    pub defined_vars: Vec<DummyVar>,
+}
+
+pub struct ExprBuilder {
+    expr: DummyExpr,
+}
+
+impl ExprBuilder {
+    pub fn apply(inst: ApplyTarget, args: Vec<DummyExpr>) -> Self {
+        let expr = DummyExpr::Apply(inst, args);
+        Self { expr }
+    }
+
+    pub fn assign_to(self, defined_vars: Vec<DummyVar>) -> DummyDef {
+        DummyDef {
+            expr: self.expr,
+            defined_vars,
+        }
+    }
+}
+
+macro_rules! def_rhs {
+    // inst(a, b, c)
+    ($inst:ident($($src:expr),*)) => {
+        ExprBuilder::apply($inst.into(), vec![$($src.clone().into()),*])
+    };
+
+    // inst.type(a, b, c)
+    ($inst:ident.$type:ident($($src:expr),*)) => {
+        ExprBuilder::apply(bind($inst, $type).into(), vec![$($src.clone().into()),*])
+    };
+}
+
+// Helper macro to define legalization recipes.
+macro_rules! def {
+    // x = ...
+    ($dest:ident = $($tt:tt)*) => {
+        def_rhs!($($tt)*).assign_to(vec![$dest.clone()])
+    };
+
+    // (x, y, ...) = ...
+    (($($dest:ident),*) = $($tt:tt)*) => {
+        def_rhs!($($tt)*).assign_to(vec![$($dest.clone()),*])
+    };
+
+    // An instruction with no results.
+    ($($tt:tt)*) => {
+        def_rhs!($($tt)*).assign_to(Vec::new())
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/cpu_modes.rs
@@ -0,0 +1,84 @@
+use crate::cdsl::types::LaneType;
+use crate::cdsl::xform::{TransformGroup, TransformGroupIndex, TransformGroups};
+
+use std::collections::{HashMap, HashSet};
+use std::iter::FromIterator;
+
+pub struct CpuMode {
+    _name: &'static str,
+    default_legalize: Option<TransformGroupIndex>,
+    monomorphic_legalize: Option<TransformGroupIndex>,
+    typed_legalize: HashMap<String, TransformGroupIndex>,
+}
+
+impl CpuMode {
+    pub fn new(name: &'static str) -> Self {
+        Self {
+            _name: name,
+            default_legalize: None,
+            monomorphic_legalize: None,
+            typed_legalize: HashMap::new(),
+        }
+    }
+    pub fn legalize_monomorphic(&mut self, group: &TransformGroup) {
+        assert!(self.monomorphic_legalize.is_none());
+        self.monomorphic_legalize = Some(group.id);
+    }
+    pub fn legalize_default(&mut self, group: &TransformGroup) {
+        assert!(self.default_legalize.is_none());
+        self.default_legalize = Some(group.id);
+    }
+    pub fn legalize_type(&mut self, lane_type: impl Into<LaneType>, group: &TransformGroup) {
+        assert!(self
+            .typed_legalize
+            .insert(lane_type.into().to_string(), group.id)
+            .is_none());
+    }
+
+    /// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the
+    /// transitive set of TransformGroup this TargetIsa uses.
+    pub fn transitive_transform_groups(
+        &self,
+        all_groups: &TransformGroups,
+    ) -> Vec<TransformGroupIndex> {
+        let mut roots = Vec::new();
+        if let Some(i) = &self.default_legalize {
+            roots.push(*i);
+        }
+        if let Some(i) = &self.monomorphic_legalize {
+            roots.push(*i);
+        }
+        roots.extend(self.typed_legalize.values().cloned());
+
+        let mut set = HashSet::new();
+        for root in roots {
+            set.insert(root);
+            let mut base = root;
+            // Follow the chain of chain_with.
+            while let Some(chain_with) = &all_groups.get(base).chain_with {
+                set.insert(*chain_with);
+                base = *chain_with;
+            }
+        }
+
+        let mut ret = Vec::from_iter(set);
+        ret.sort();
+        ret
+    }
+
+    /// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the directly
+    /// reachable set of TransformGroup this TargetIsa uses.
+    pub fn direct_transform_groups(&self) -> Vec<TransformGroupIndex> {
+        let mut set = HashSet::new();
+        if let Some(i) = &self.default_legalize {
+            set.insert(*i);
+        }
+        if let Some(i) = &self.monomorphic_legalize {
+            set.insert(*i);
+        }
+        set.extend(self.typed_legalize.values().cloned());
+        let mut ret = Vec::from_iter(set);
+        ret.sort();
+        ret
+    }
+}
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/formats.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/formats.rs
@@ -5,61 +5,54 @@ use std::fmt;
 use std::slice;
 
 use cranelift_entity::{entity_impl, PrimaryMap};
 
 /// An immediate field in an instruction format.
 ///
 /// This corresponds to a single member of a variant of the `InstructionData`
 /// data type.
-///
-/// :param iform: Parent `InstructionFormat`.
-/// :param kind: Immediate Operand kind.
-/// :param member: Member name in `InstructionData` variant.
 #[derive(Debug)]
 pub struct FormatField {
     /// Immediate operand number in parent.
     immnum: usize,
 
     /// Immediate operand kind.
     pub kind: OperandKind,
 
-    /// Member name in InstructionDate variant.
+    /// Member name in InstructionData variant.
     pub member: &'static str,
 }
 
-/// Every instruction opcode has a corresponding instruction format which
-/// determines the number of operands and their kinds. Instruction formats are
-/// identified structurally, i.e., the format of an instruction is derived from
-/// the kinds of operands used in its declaration.
+/// Every instruction opcode has a corresponding instruction format which determines the number of
+/// operands and their kinds. Instruction formats are identified structurally, i.e., the format of
+/// an instruction is derived from the kinds of operands used in its declaration.
 ///
-/// The instruction format stores two separate lists of operands: Immediates
-/// and values. Immediate operands (including entity references) are
-/// represented as explicit members in the `InstructionData` variants. The
-/// value operands are stored differently, depending on how many there are.
-/// Beyond a certain point, instruction formats switch to an external value
-/// list for storing value arguments. Value lists can hold an arbitrary number
-/// of values.
+/// The instruction format stores two separate lists of operands: Immediates and values. Immediate
+/// operands (including entity references) are represented as explicit members in the
+/// `InstructionData` variants. The value operands are stored differently, depending on how many
+/// there are.  Beyond a certain point, instruction formats switch to an external value list for
+/// storing value arguments. Value lists can hold an arbitrary number of values.
 ///
-/// All instruction formats must be predefined in the meta shared/formats module.
-///
-/// :param kinds: List of `OperandKind` objects describing the operands.
-/// :param name: Instruction format name in CamelCase. This is used as a Rust
-///     variant name in both the `InstructionData` and `InstructionFormat`
-///     enums.
-/// :param typevar_operand: Index of the value input operand that is used to
-///     infer the controlling type variable. By default, this is `0`, the first
-///     `value` operand. The index is relative to the values only, ignoring
-///     immediate operands.
+/// All instruction formats must be predefined in the meta shared/formats.rs module.
 #[derive(Debug)]
 pub struct InstructionFormat {
+    /// Instruction format name in CamelCase. This is used as a Rust variant name in both the
+    /// `InstructionData` and `InstructionFormat` enums.
     pub name: &'static str,
+
     pub num_value_operands: usize,
+
     pub has_value_list: bool,
+
     pub imm_fields: Vec<FormatField>,
+
+    /// Index of the value input operand that is used to infer the controlling type variable. By
+    /// default, this is `0`, the first `value` operand. The index is relative to the values only,
+    /// ignoring immediate operands.
     pub typevar_operand: Option<usize>,
 }
 
 impl fmt::Display for InstructionFormat {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
         let args = self
             .imm_fields
             .iter()
@@ -157,17 +150,17 @@ impl InstructionFormatBuilder {
             num_value_operands: self.num_value_operands,
             has_value_list: self.has_value_list,
             imm_fields: self.imm_fields,
             typevar_operand,
         }
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct InstructionFormatIndex(u32);
 entity_impl!(InstructionFormatIndex);
 
 pub struct FormatRegistry {
     /// Map (immediate kinds names, number of values, has varargs) to an instruction format index
     /// in the actual map.
     sig_to_index: HashMap<(Vec<String>, usize, bool), InstructionFormatIndex>,
     map: PrimaryMap<InstructionFormatIndex, InstructionFormat>,
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/inst.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/inst.rs
@@ -1,15 +1,20 @@
 use crate::cdsl::camel_case;
-use crate::cdsl::formats::{FormatRegistry, InstructionFormat, InstructionFormatIndex};
+use crate::cdsl::formats::{
+    FormatField, FormatRegistry, InstructionFormat, InstructionFormatIndex,
+};
 use crate::cdsl::operands::Operand;
 use crate::cdsl::type_inference::Constraint;
+use crate::cdsl::types::ValueType;
 use crate::cdsl::typevar::TypeVar;
 
 use std::fmt;
+use std::ops;
+use std::rc::Rc;
 use std::slice;
 
 /// Every instruction must belong to exactly one instruction group. A given
 /// target architecture can support instructions from multiple groups, and it
 /// does not necessarily support all instructions in a group.
 pub struct InstructionGroup {
     _name: &'static str,
     _doc: &'static str,
@@ -27,38 +32,45 @@ impl InstructionGroup {
 
     pub fn push(&mut self, inst: Instruction) {
         self.instructions.push(inst);
     }
 
     pub fn iter(&self) -> slice::Iter<Instruction> {
         self.instructions.iter()
     }
+
+    pub fn by_name(&self, name: &'static str) -> &Instruction {
+        self.instructions
+            .iter()
+            .find(|inst| inst.name == name)
+            .expect(&format!("unexisting instruction with name {}", name))
+    }
 }
 
 pub struct PolymorphicInfo {
     pub use_typevar_operand: bool,
     pub ctrl_typevar: TypeVar,
     pub other_typevars: Vec<TypeVar>,
 }
 
-pub struct Instruction {
+pub struct InstructionContent {
     /// Instruction mnemonic, also becomes opcode name.
     pub name: &'static str,
     pub camel_name: String,
 
     /// Documentation string.
     doc: &'static str,
 
     /// Input operands. This can be a mix of SSA value operands and other operand kinds.
     pub operands_in: Vec<Operand>,
     /// Output operands. The output operands must be SSA values or `variable_args`.
     pub operands_out: Vec<Operand>,
     /// Instruction-specific TypeConstraints.
-    _constraints: Vec<Constraint>,
+    pub constraints: Vec<Constraint>,
 
     /// Instruction format, automatically derived from the input operands.
     pub format: InstructionFormatIndex,
 
     /// One of the input or output operands is a free type variable. None if the instruction is not
     /// polymorphic, set otherwise.
     pub polymorphic_info: Option<PolymorphicInfo>,
 
@@ -85,16 +97,28 @@ pub struct Instruction {
     /// Can this instruction cause a trap?
     pub can_trap: bool,
     /// Does this instruction have other side effects besides can_* flags?
     pub other_side_effects: bool,
     /// Does this instruction write to CPU flags?
     pub writes_cpu_flags: bool,
 }
 
+#[derive(Clone)]
+pub struct Instruction {
+    content: Rc<InstructionContent>,
+}
+
+impl ops::Deref for Instruction {
+    type Target = InstructionContent;
+    fn deref(&self) -> &Self::Target {
+        &*self.content
+    }
+}
+
 impl Instruction {
     pub fn snake_name(&self) -> &'static str {
         if self.name == "return" {
             "return_"
         } else {
             self.name
         }
     }
@@ -103,16 +127,27 @@ impl Instruction {
         for line in self.doc.split("\n") {
             let stripped = line.trim();
             if stripped.len() > 0 {
                 return stripped;
             }
         }
         ""
     }
+
+    pub fn all_typevars(&self) -> Vec<&TypeVar> {
+        match &self.polymorphic_info {
+            Some(poly) => {
+                let mut result = vec![&poly.ctrl_typevar];
+                result.extend(&poly.other_typevars);
+                result
+            }
+            None => Vec::new(),
+        }
+    }
 }
 
 impl fmt::Display for Instruction {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
         if self.operands_out.len() > 0 {
             let operands_out = self
                 .operands_out
                 .iter()
@@ -267,42 +302,50 @@ impl InstructionBuilder {
         let format = format_registry.get(format_index);
         let polymorphic_info =
             verify_polymorphic(&operands_in, &operands_out, &format, &value_opnums);
 
         // Infer from output operands whether an instruciton clobbers CPU flags or not.
         let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
 
         Instruction {
-            name: self.name,
-            camel_name: camel_case(self.name),
-            doc: self.doc,
-            operands_in,
-            operands_out,
-            _constraints: self.constraints.unwrap_or_else(Vec::new),
-            format: format_index,
-            polymorphic_info,
-            value_opnums,
-            value_results,
-            imm_opnums,
-            is_terminator: self.is_terminator,
-            is_branch: self.is_branch,
-            is_indirect_branch: self.is_indirect_branch,
-            is_call: self.is_call,
-            is_return: self.is_return,
-            is_ghost: self.is_ghost,
-            can_load: self.can_load,
-            can_store: self.can_store,
-            can_trap: self.can_trap,
-            other_side_effects: self.other_side_effects,
-            writes_cpu_flags,
+            content: Rc::new(InstructionContent {
+                name: self.name,
+                camel_name: camel_case(self.name),
+                doc: self.doc,
+                operands_in,
+                operands_out,
+                constraints: self.constraints.unwrap_or_else(Vec::new),
+                format: format_index,
+                polymorphic_info,
+                value_opnums,
+                value_results,
+                imm_opnums,
+                is_terminator: self.is_terminator,
+                is_branch: self.is_branch,
+                is_indirect_branch: self.is_indirect_branch,
+                is_call: self.is_call,
+                is_return: self.is_return,
+                is_ghost: self.is_ghost,
+                can_load: self.can_load,
+                can_store: self.can_store,
+                can_trap: self.can_trap,
+                other_side_effects: self.other_side_effects,
+                writes_cpu_flags,
+            }),
         }
     }
 }
 
+#[derive(Clone)]
+pub struct BoundInstruction {
+    pub inst: Instruction,
+    pub value_types: Vec<ValueType>,
+}
+
 /// Check if this instruction is polymorphic, and verify its use of type variables.
 fn verify_polymorphic(
     operands_in: &Vec<Operand>,
     operands_out: &Vec<Operand>,
     format: &InstructionFormat,
     value_opnums: &Vec<usize>,
 ) -> Option<PolymorphicInfo> {
     // The instruction is polymorphic if it has one free input or output operand.
@@ -451,8 +494,107 @@ fn verify_ctrl_typevar(
             continue;
         }
 
         return Err("type variable in output not derived from ctrl_typevar".into());
     }
 
     Ok(other_typevars)
 }
+
+/// A basic node in an instruction predicate: either an atom, or an AND of two conditions.
+pub enum InstructionPredicateNode {
+    /// Is the field member (first member) equal to the actual argument (which name is the second
+    /// field)?
+    IsFieldEqual(String, String),
+
+    /// Is the value argument (at the index designated by the first member) the same type as the
+    /// type name (second member)?
+    TypeVarCheck(usize, String),
+
+    /// Is the controlling type variable the same type as the one designated by the type name
+    /// (only member)?
+    CtrlTypeVarCheck(String),
+
+    /// A combination of two other predicates.
+    And(Vec<InstructionPredicateNode>),
+}
+
+impl InstructionPredicateNode {
+    fn rust_predicate(&self) -> String {
+        match self {
+            InstructionPredicateNode::IsFieldEqual(field_name, arg) => {
+                let new_args = vec![field_name.clone(), arg.clone()];
+                format!("crate::predicates::is_equal({})", new_args.join(", "))
+            }
+            InstructionPredicateNode::TypeVarCheck(index, value_type_name) => format!(
+                "func.dfg.value_type(args[{}]) == {}",
+                index, value_type_name
+            ),
+            InstructionPredicateNode::CtrlTypeVarCheck(value_type_name) => {
+                format!("func.dfg.ctrl_typevar(inst) == {}", value_type_name)
+            }
+            InstructionPredicateNode::And(nodes) => nodes
+                .iter()
+                .map(|x| x.rust_predicate())
+                .collect::<Vec<_>>()
+                .join(" &&\n"),
+        }
+    }
+}
+
+pub struct InstructionPredicate {
+    node: Option<InstructionPredicateNode>,
+}
+
+impl InstructionPredicate {
+    pub fn new() -> Self {
+        Self { node: None }
+    }
+
+    pub fn new_typevar_check(
+        inst: &Instruction,
+        type_var: &TypeVar,
+        value_type: &ValueType,
+    ) -> InstructionPredicateNode {
+        let index = inst
+            .value_opnums
+            .iter()
+            .enumerate()
+            .filter(|(_, &op_num)| inst.operands_in[op_num].type_var().unwrap() == type_var)
+            .next()
+            .unwrap()
+            .0;
+        InstructionPredicateNode::TypeVarCheck(index, value_type.rust_name())
+    }
+
+    pub fn new_is_field_equal(
+        format_field: &FormatField,
+        imm_value: String,
+    ) -> InstructionPredicateNode {
+        InstructionPredicateNode::IsFieldEqual(format_field.member.into(), imm_value)
+    }
+
+    pub fn new_ctrl_typevar_check(value_type: &ValueType) -> InstructionPredicateNode {
+        InstructionPredicateNode::CtrlTypeVarCheck(value_type.rust_name())
+    }
+
+    pub fn and(mut self, new_node: InstructionPredicateNode) -> Self {
+        let node = self.node;
+        let mut and_nodes = match node {
+            Some(node) => match node {
+                InstructionPredicateNode::And(nodes) => nodes,
+                _ => vec![node],
+            },
+            _ => Vec::new(),
+        };
+        and_nodes.push(new_node);
+        self.node = Some(InstructionPredicateNode::And(and_nodes));
+        self
+    }
+
+    pub fn rust_predicate(&self) -> String {
+        match &self.node {
+            Some(root) => root.rust_predicate(),
+            None => "true".into(),
+        }
+    }
+}
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/isa.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/isa.rs
@@ -1,26 +1,61 @@
+use crate::cdsl::cpu_modes::CpuMode;
 use crate::cdsl::inst::InstructionGroup;
 use crate::cdsl::regs::IsaRegs;
 use crate::cdsl::settings::SettingGroup;
+use crate::cdsl::xform::{TransformGroupIndex, TransformGroups};
+
+use std::collections::HashSet;
+use std::iter::FromIterator;
 
 pub struct TargetIsa {
     pub name: &'static str,
     pub instructions: InstructionGroup,
     pub settings: SettingGroup,
     pub regs: IsaRegs,
+    pub cpu_modes: Vec<CpuMode>,
 }
 
 impl TargetIsa {
     pub fn new(
         name: &'static str,
         instructions: InstructionGroup,
         settings: SettingGroup,
         regs: IsaRegs,
+        cpu_modes: Vec<CpuMode>,
     ) -> Self {
         Self {
             name,
             instructions,
             settings,
             regs,
+            cpu_modes,
         }
     }
+
+    /// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the
+    /// transitive set of TransformGroup this TargetIsa uses.
+    pub fn transitive_transform_groups(
+        &self,
+        all_groups: &TransformGroups,
+    ) -> Vec<TransformGroupIndex> {
+        let mut set = HashSet::new();
+        for cpu_mode in &self.cpu_modes {
+            set.extend(cpu_mode.transitive_transform_groups(all_groups));
+        }
+        let mut vec = Vec::from_iter(set);
+        vec.sort();
+        vec
+    }
+
+    /// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the directly
+    /// reachable set of TransformGroup this TargetIsa uses.
+    pub fn direct_transform_groups(&self) -> Vec<TransformGroupIndex> {
+        let mut set = HashSet::new();
+        for cpu_mode in &self.cpu_modes {
+            set.extend(cpu_mode.direct_transform_groups());
+        }
+        let mut vec = Vec::from_iter(set);
+        vec.sort();
+        vec
+    }
 }
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/mod.rs
@@ -1,29 +1,42 @@
 //! Cranelift DSL classes.
 //!
 //! This module defines the classes that are used to define Cranelift
 //! instructions and other entities.
 
+#[macro_use]
+pub mod ast;
+pub mod cpu_modes;
 pub mod formats;
 pub mod inst;
 pub mod isa;
 pub mod operands;
 pub mod regs;
 pub mod settings;
 pub mod type_inference;
 pub mod types;
 pub mod typevar;
+pub mod xform;
 
 /// A macro that converts boolean settings into predicates to look more natural.
 #[macro_export]
 macro_rules! predicate {
     ($a:ident && $($b:tt)*) => {
         PredicateNode::And(Box::new($a.into()), Box::new(predicate!($($b)*)))
     };
+    (!$a:ident && $($b:tt)*) => {
+        PredicateNode::And(
+            Box::new(PredicateNode::Not(Box::new($a.into()))),
+            Box::new(predicate!($($b)*))
+        )
+    };
+    (!$a:ident) => {
+        PredicateNode::Not(Box::new($a.into()))
+    };
     ($a:ident) => {
         $a.into()
     };
 }
 
 #[macro_export]
 macro_rules! preset {
     () => {
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/operands.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/operands.rs
@@ -129,17 +129,17 @@ pub struct OperandKind {
 
     doc: Option<String>,
 
     pub default_member: Option<&'static str>,
 
     /// The camel-cased name of an operand kind is also the Rust type used to represent it.
     pub rust_type: String,
 
-    fields: OperandKindFields,
+    pub fields: OperandKindFields,
 }
 
 impl OperandKind {
     pub fn imm_key(&self) -> Option<String> {
         match self.fields {
             OperandKindFields::ImmEnum(_)
             | OperandKindFields::ImmValue
             | OperandKindFields::EntityRef => Some(self.name.to_string()),
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/settings.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/settings.rs
@@ -148,32 +148,33 @@ impl SettingGroup {
         }
         panic!("Should have found bool setting by name.");
     }
 }
 
 /// This is the basic information needed to track the specific parts of a setting when building
 /// them.
 pub enum ProtoSpecificSetting {
-    Bool(bool, u8),
+    Bool(bool),
     Enum(Vec<&'static str>),
     Num(u8),
 }
 
 /// This is the information provided during building for a setting.
 struct ProtoSetting {
     name: &'static str,
     comment: &'static str,
     specific: ProtoSpecificSetting,
 }
 
 #[derive(Hash, PartialEq, Eq)]
 pub enum PredicateNode {
     OwnedBool(BoolSettingIndex),
     SharedBool(&'static str, &'static str),
+    Not(Box<PredicateNode>),
     And(Box<PredicateNode>, Box<PredicateNode>),
 }
 
 impl Into<PredicateNode> for BoolSettingIndex {
     fn into(self) -> PredicateNode {
         PredicateNode::OwnedBool(self)
     }
 }
@@ -193,48 +194,52 @@ impl PredicateNode {
                 group.name, group.settings[bool_setting_index.0].name
             ),
             PredicateNode::SharedBool(ref group_name, ref bool_name) => {
                 format!("{}.{}()", group_name, bool_name)
             }
             PredicateNode::And(ref lhs, ref rhs) => {
                 format!("{} && {}", lhs.render(group), rhs.render(group))
             }
+            PredicateNode::Not(ref node) => format!("!({})", node.render(group)),
         }
     }
 }
 
+struct ProtoPredicate {
+    pub name: &'static str,
+    node: PredicateNode,
+}
+
 pub struct Predicate {
     pub name: &'static str,
     node: PredicateNode,
     pub number: u8,
 }
 
 impl Predicate {
     pub fn render(&self, group: &SettingGroup) -> String {
         self.node.render(group)
     }
 }
 
 pub struct SettingGroupBuilder {
     name: &'static str,
     settings: Vec<ProtoSetting>,
     presets: Vec<Preset>,
-    predicates: Vec<Predicate>,
-    predicate_number: u8,
+    predicates: Vec<ProtoPredicate>,
 }
 
 impl SettingGroupBuilder {
     pub fn new(name: &'static str) -> Self {
         Self {
             name,
             settings: Vec::new(),
             presets: Vec::new(),
             predicates: Vec::new(),
-            predicate_number: 0,
         }
     }
 
     fn add_setting(
         &mut self,
         name: &'static str,
         comment: &'static str,
         specific: ProtoSpecificSetting,
@@ -251,23 +256,17 @@ impl SettingGroupBuilder {
         name: &'static str,
         comment: &'static str,
         default: bool,
     ) -> BoolSettingIndex {
         assert!(
             self.predicates.len() == 0,
             "predicates must be added after the boolean settings"
         );
-        let predicate_number = self.predicate_number;
-        self.predicate_number += 1;
-        self.add_setting(
-            name,
-            comment,
-            ProtoSpecificSetting::Bool(default, predicate_number),
-        );
+        self.add_setting(name, comment, ProtoSpecificSetting::Bool(default));
         BoolSettingIndex(self.settings.len() - 1)
     }
 
     pub fn add_enum(
         &mut self,
         name: &'static str,
         comment: &'static str,
         values: Vec<&'static str>,
@@ -275,19 +274,17 @@ impl SettingGroupBuilder {
         self.add_setting(name, comment, ProtoSpecificSetting::Enum(values));
     }
 
     pub fn add_num(&mut self, name: &'static str, comment: &'static str, default: u8) {
         self.add_setting(name, comment, ProtoSpecificSetting::Num(default));
     }
 
     pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
-        let number = self.predicate_number;
-        self.predicate_number += 1;
-        self.predicates.push(Predicate { name, node, number });
+        self.predicates.push(ProtoPredicate { name, node });
     }
 
     pub fn add_preset(&mut self, name: &'static str, args: Vec<PresetType>) -> PresetIndex {
         let mut values = Vec::new();
         for arg in args {
             match arg {
                 PresetType::OtherPreset(index) => {
                     values.extend(self.presets[index.0].values.iter());
@@ -302,30 +299,27 @@ impl SettingGroupBuilder {
     /// Compute the layout of the byte vector used to represent this settings
     /// group.
     ///
     /// The byte vector contains the following entries in order:
     ///
     /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
     /// 2. `BoolSetting` settings.
     /// 3. Precomputed named predicates.
-    /// 4. Other numbered predicates, including anonymous predicates and parent
-    ///    predicates that need to be accessible by number.
+    /// 4. Other numbered predicates, including parent predicates that need to be accessible by
+    ///    number.
     ///
     /// Set `self.settings_size` to the length of the byte vector prefix that
     /// contains the settings. All bytes after that are computed, not
     /// configured.
     ///
     /// Set `self.boolean_offset` to the beginning of the numbered predicates,
     /// 2. in the list above.
     ///
     /// Assign `byte_offset` and `bit_offset` fields in all settings.
-    ///
-    /// After calling this method, no more settings can be added, but
-    /// additional predicates can be made accessible with `number_predicate()`.
     pub fn finish(self) -> SettingGroup {
         let mut group = SettingGroup {
             name: self.name,
             settings: Vec::new(),
             bool_start_byte_offset: 0,
             settings_size: 0,
             presets: Vec::new(),
             predicates: Vec::new(),
@@ -348,40 +342,56 @@ impl SettingGroupBuilder {
                 specific,
             });
 
             byte_offset += 1;
         }
 
         group.bool_start_byte_offset = byte_offset;
 
+        let mut predicate_number = 0;
+
         // Then the boolean settings.
         for s in &self.settings {
-            let (default, predicate_number) = match s.specific {
-                ProtoSpecificSetting::Bool(default, predicate_number) => {
-                    (default, predicate_number)
-                }
+            let default = match s.specific {
+                ProtoSpecificSetting::Bool(default) => default,
                 ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
             };
             group.settings.push(Setting {
                 name: s.name,
                 comment: s.comment,
                 byte_offset: byte_offset + predicate_number / 8,
                 specific: SpecificSetting::Bool(BoolSetting {
                     default,
                     bit_offset: predicate_number % 8,
                     predicate_number,
                 }),
             });
+            predicate_number += 1;
         }
 
         assert!(
             group.predicates.len() == 0,
             "settings_size is the byte size before adding predicates"
         );
         group.settings_size = group.byte_size();
 
-        group.predicates.extend(self.predicates);
+        // Sort predicates by name to ensure the same order as the Python code.
+        let mut predicates = self.predicates;
+        predicates.sort_by_key(|predicate| predicate.name);
+
+        group
+            .predicates
+            .extend(predicates.into_iter().map(|predicate| {
+                let number = predicate_number;
+                predicate_number += 1;
+                return Predicate {
+                    name: predicate.name,
+                    node: predicate.node,
+                    number,
+                };
+            }));
+
         group.presets.extend(self.presets);
 
         group
     }
 }
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/type_inference.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/type_inference.rs
@@ -1,5 +1,658 @@
-use crate::cdsl::typevar::TypeVar;
+use crate::cdsl::ast::{Def, DefIndex, DefPool, Var, VarIndex, VarPool};
+use crate::cdsl::typevar::{DerivedFunc, TypeSet, TypeVar};
 
+use std::collections::{HashMap, HashSet};
+use std::iter::FromIterator;
+
+#[derive(Hash, PartialEq, Eq)]
 pub enum Constraint {
+    /// Constraint specifying that a type var tv1 must be wider than or equal to type var tv2 at
+    /// runtime. This requires that:
+    /// 1) They have the same number of lanes
+    /// 2) In a lane tv1 has at least as many bits as tv2.
     WiderOrEq(TypeVar, TypeVar),
+
+    /// Constraint specifying that two derived type vars must have the same runtime type.
+    Eq(TypeVar, TypeVar),
+
+    /// Constraint specifying that a type var must belong to some typeset.
+    InTypeset(TypeVar, TypeSet),
 }
+
+impl Constraint {
+    fn translate_with<F: Fn(&TypeVar) -> TypeVar>(&self, func: F) -> Constraint {
+        match self {
+            Constraint::WiderOrEq(lhs, rhs) => {
+                let lhs = func(&lhs);
+                let rhs = func(&rhs);
+                Constraint::WiderOrEq(lhs, rhs)
+            }
+            Constraint::Eq(lhs, rhs) => {
+                let lhs = func(&lhs);
+                let rhs = func(&rhs);
+                Constraint::Eq(lhs, rhs)
+            }
+            Constraint::InTypeset(tv, ts) => {
+                let tv = func(&tv);
+                Constraint::InTypeset(tv, ts.clone())
+            }
+        }
+    }
+
+    /// Creates a new constraint by replacing type vars by their hashmap equivalent.
+    fn translate_with_map(
+        &self,
+        original_to_own_typevar: &HashMap<&TypeVar, TypeVar>,
+    ) -> Constraint {
+        self.translate_with(|tv| substitute(original_to_own_typevar, tv))
+    }
+
+    /// Creates a new constraint by replacing type vars by their canonical equivalent.
+    fn translate_with_env(&self, type_env: &TypeEnvironment) -> Constraint {
+        self.translate_with(|tv| type_env.get_equivalent(tv))
+    }
+
+    fn is_trivial(&self) -> bool {
+        match self {
+            Constraint::WiderOrEq(lhs, rhs) => {
+                // Trivially true.
+                if lhs == rhs {
+                    return true;
+                }
+
+                let ts1 = lhs.get_typeset();
+                let ts2 = rhs.get_typeset();
+
+                // Trivially true.
+                if ts1.is_wider_or_equal(&ts2) {
+                    return true;
+                }
+
+                // Trivially false.
+                if ts1.is_narrower(&ts2) {
+                    return true;
+                }
+
+                // Trivially false.
+                if (&ts1.lanes & &ts2.lanes).len() == 0 {
+                    return true;
+                }
+
+                self.is_concrete()
+            }
+            Constraint::Eq(lhs, rhs) => lhs == rhs || self.is_concrete(),
+            Constraint::InTypeset(_, _) => {
+                // The way InTypeset are made, they would always be trivial if we were applying the
+                // same logic as the Python code did, so ignore this.
+                self.is_concrete()
+            }
+        }
+    }
+
+    /// Returns true iff all the referenced type vars are singletons.
+    fn is_concrete(&self) -> bool {
+        match self {
+            Constraint::WiderOrEq(lhs, rhs) => {
+                lhs.singleton_type().is_some() && rhs.singleton_type().is_some()
+            }
+            Constraint::Eq(lhs, rhs) => {
+                lhs.singleton_type().is_some() && rhs.singleton_type().is_some()
+            }
+            Constraint::InTypeset(tv, _) => tv.singleton_type().is_some(),
+        }
+    }
+
+    fn typevar_args(&self) -> Vec<&TypeVar> {
+        match self {
+            Constraint::WiderOrEq(lhs, rhs) => vec![lhs, rhs],
+            Constraint::Eq(lhs, rhs) => vec![lhs, rhs],
+            Constraint::InTypeset(tv, _) => vec![tv],
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+enum TypeEnvRank {
+    Singleton = 5,
+    Input = 4,
+    Intermediate = 3,
+    Output = 2,
+    Temp = 1,
+    Internal = 0,
+}
+
+/// Class encapsulating the necessary bookkeeping for type inference.
+pub struct TypeEnvironment {
+    vars: HashSet<VarIndex>,
+    ranks: HashMap<TypeVar, TypeEnvRank>,
+    equivalency_map: HashMap<TypeVar, TypeVar>,
+    pub constraints: Vec<Constraint>,
+}
+
+impl TypeEnvironment {
+    fn new() -> Self {
+        TypeEnvironment {
+            vars: HashSet::new(),
+            ranks: HashMap::new(),
+            equivalency_map: HashMap::new(),
+            constraints: Vec::new(),
+        }
+    }
+
+    fn register(&mut self, var_index: VarIndex, var: &mut Var) {
+        self.vars.insert(var_index);
+        let rank = if var.is_input() {
+            TypeEnvRank::Input
+        } else if var.is_intermediate() {
+            TypeEnvRank::Intermediate
+        } else if var.is_output() {
+            TypeEnvRank::Output
+        } else {
+            assert!(var.is_temp());
+            TypeEnvRank::Temp
+        };
+        self.ranks.insert(var.get_or_create_typevar(), rank);
+    }
+
+    fn add_constraint(&mut self, constraint: Constraint) {
+        if self
+            .constraints
+            .iter()
+            .find(|&item| item == &constraint)
+            .is_some()
+        {
+            return;
+        }
+
+        // Check extra conditions for InTypeset constraints.
+        if let Constraint::InTypeset(tv, _) = &constraint {
+            assert!(tv.base.is_none());
+            assert!(tv.name.starts_with("typeof_"));
+        }
+
+        self.constraints.push(constraint);
+    }
+
+    /// Returns the canonical representative of the equivalency class of the given argument, or
+    /// duplicates it if it's not there yet.
+    pub fn get_equivalent(&self, tv: &TypeVar) -> TypeVar {
+        let mut tv = tv;
+        while let Some(found) = self.equivalency_map.get(tv) {
+            tv = found;
+        }
+        match &tv.base {
+            Some(parent) => self
+                .get_equivalent(&parent.type_var)
+                .derived(parent.derived_func),
+            None => tv.clone(),
+        }
+    }
+
+    /// Get the rank of tv in the partial order:
+    /// - TVs directly associated with a Var get their rank from the Var (see register()).
+    /// - Internally generated non-derived TVs implicitly get the lowest rank (0).
+    /// - Derived variables get their rank from their free typevar.
+    /// - Singletons have the highest rank.
+    /// - TVs associated with vars in a source pattern have a higher rank than TVs associated with
+    /// temporary vars.
+    fn rank(&self, tv: &TypeVar) -> u8 {
+        let actual_tv = match tv.base {
+            Some(_) => tv.free_typevar(),
+            None => Some(tv.clone()),
+        };
+
+        let rank = match actual_tv {
+            Some(actual_tv) => match self.ranks.get(&actual_tv) {
+                Some(rank) => Some(*rank),
+                None => {
+                    assert!(
+                        !actual_tv.name.starts_with("typeof_"),
+                        format!("variable {} should be explicitly ranked", actual_tv.name)
+                    );
+                    None
+                }
+            },
+            None => None,
+        };
+
+        let rank = match rank {
+            Some(rank) => rank,
+            None => {
+                if tv.singleton_type().is_some() {
+                    TypeEnvRank::Singleton
+                } else {
+                    TypeEnvRank::Internal
+                }
+            }
+        };
+
+        rank as u8
+    }
+
+    /// Record the fact that the free tv1 is part of the same equivalence class as tv2. The
+    /// canonical representative of the merged class is tv2's canonical representative.
+    fn record_equivalent(&mut self, tv1: TypeVar, tv2: TypeVar) {
+        assert!(tv1.base.is_none());
+        assert!(self.get_equivalent(&tv1) == tv1);
+        if let Some(tv2_base) = &tv2.base {
+            // Ensure there are no cycles.
+            assert!(self.get_equivalent(&tv2_base.type_var) != tv1);
+        }
+        self.equivalency_map.insert(tv1, tv2);
+    }
+
+    /// Get the free typevars in the current type environment.
+    pub fn free_typevars(&self, var_pool: &mut VarPool) -> Vec<TypeVar> {
+        let mut typevars = Vec::new();
+        typevars.extend(self.equivalency_map.keys().cloned());
+        typevars.extend(
+            self.vars
+                .iter()
+                .map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
+        );
+
+        let set: HashSet<TypeVar> = HashSet::from_iter(
+            typevars
+                .iter()
+                .map(|tv| self.get_equivalent(tv).free_typevar())
+                .filter(|opt_tv| {
+                    // Filter out singleton types.
+                    return opt_tv.is_some();
+                })
+                .map(|tv| tv.unwrap()),
+        );
+        Vec::from_iter(set)
+    }
+
+    /// Normalize by collapsing any roots that don't correspond to a concrete type var AND have a
+    /// single type var derived from them or equivalent to them.
+    ///
+    /// e.g. if we have a root of the tree that looks like:
+    ///
+    ///   typeof_a   typeof_b
+    ///          \\  /
+    ///       typeof_x
+    ///           |
+    ///         half_width(1)
+    ///           |
+    ///           1
+    ///
+    /// we want to collapse the linear path between 1 and typeof_x. The resulting graph is:
+    ///
+    ///   typeof_a   typeof_b
+    ///          \\  /
+    ///       typeof_x
+    fn normalize(&mut self, var_pool: &mut VarPool) {
+        let source_tvs: HashSet<TypeVar> = HashSet::from_iter(
+            self.vars
+                .iter()
+                .map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
+        );
+
+        let mut children: HashMap<TypeVar, HashSet<TypeVar>> = HashMap::new();
+
+        // Insert all the parents found by the derivation relationship.
+        for type_var in self.equivalency_map.values() {
+            if type_var.base.is_none() {
+                continue;
+            }
+
+            let parent_tv = type_var.free_typevar();
+            if parent_tv.is_none() {
+                // Ignore this type variable, it's a singleton.
+                continue;
+            }
+            let parent_tv = parent_tv.unwrap();
+
+            children
+                .entry(parent_tv)
+                .or_insert(HashSet::new())
+                .insert(type_var.clone());
+        }
+
+        // Insert all the explicit equivalency links.
+        for (equivalent_tv, canon_tv) in self.equivalency_map.iter() {
+            children
+                .entry(canon_tv.clone())
+                .or_insert(HashSet::new())
+                .insert(equivalent_tv.clone());
+        }
+
+        // Remove links that are straight paths up to typevar of variables.
+        for free_root in self.free_typevars(var_pool) {
+            let mut root = &free_root;
+            while !source_tvs.contains(&root)
+                && children.contains_key(&root)
+                && children.get(&root).unwrap().len() == 1
+            {
+                let child = children.get(&root).unwrap().iter().next().unwrap();
+                assert_eq!(self.equivalency_map[child], root.clone());
+                self.equivalency_map.remove(child);
+                root = child;
+            }
+        }
+    }
+
+    /// Extract a clean type environment from self, that only mentions type vars associated with
+    /// real variables.
+    fn extract(self, var_pool: &mut VarPool) -> TypeEnvironment {
+        let vars_tv: HashSet<TypeVar> = HashSet::from_iter(
+            self.vars
+                .iter()
+                .map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
+        );
+
+        let mut new_equivalency_map: HashMap<TypeVar, TypeVar> = HashMap::new();
+        for tv in &vars_tv {
+            let canon_tv = self.get_equivalent(tv);
+            if *tv != canon_tv {
+                new_equivalency_map.insert(tv.clone(), canon_tv.clone());
+            }
+
+            // Sanity check: the translated type map should only refer to real variables.
+            assert!(vars_tv.contains(tv));
+            let canon_free_tv = canon_tv.free_typevar();
+            assert!(canon_free_tv.is_none() || vars_tv.contains(&canon_free_tv.unwrap()));
+        }
+
+        let mut new_constraints: HashSet<Constraint> = HashSet::new();
+        for constraint in &self.constraints {
+            let constraint = constraint.translate_with_env(&self);
+            if constraint.is_trivial() || new_constraints.contains(&constraint) {
+                continue;
+            }
+
+            // Sanity check: translated constraints should refer only to real variables.
+            for arg in constraint.typevar_args() {
+                assert!(vars_tv.contains(arg));
+                let arg_free_tv = arg.free_typevar();
+                assert!(arg_free_tv.is_none() || vars_tv.contains(&arg_free_tv.unwrap()));
+            }
+
+            new_constraints.insert(constraint);
+        }
+
+        TypeEnvironment {
+            vars: self.vars,
+            ranks: self.ranks,
+            equivalency_map: new_equivalency_map,
+            constraints: Vec::from_iter(new_constraints),
+        }
+    }
+}
+
+/// Replaces an external type variable according to the following rules:
+/// - if a local copy is present in the map, return it.
+/// - or if it's derived, create a local derived one that recursively substitutes the parent.
+/// - or return itself.
+fn substitute(map: &HashMap<&TypeVar, TypeVar>, external_type_var: &TypeVar) -> TypeVar {
+    match map.get(&external_type_var) {
+        Some(own_type_var) => own_type_var.clone(),
+        None => match &external_type_var.base {
+            Some(parent) => {
+                let parent_substitute = substitute(map, &parent.type_var);
+                TypeVar::derived(&parent_substitute, parent.derived_func)
+            }
+            None => external_type_var.clone(),
+        },
+    }
+}
+
+/// Normalize a (potentially derived) typevar using the following rules:
+///
+/// - vector and width derived functions commute
+///     {HALF,DOUBLE}VECTOR({HALF,DOUBLE}WIDTH(base)) ->
+///     {HALF,DOUBLE}WIDTH({HALF,DOUBLE}VECTOR(base))
+///
+/// - half/double pairs collapse
+///     {HALF,DOUBLE}WIDTH({DOUBLE,HALF}WIDTH(base)) -> base
+///     {HALF,DOUBLE}VECTOR({DOUBLE,HALF}VECTOR(base)) -> base
+fn canonicalize_derivations(tv: TypeVar) -> TypeVar {
+    let base = match &tv.base {
+        Some(base) => base,
+        None => return tv,
+    };
+
+    let derived_func = base.derived_func;
+
+    if let Some(base_base) = &base.type_var.base {
+        let base_base_tv = &base_base.type_var;
+        match (derived_func, base_base.derived_func) {
+            (DerivedFunc::HalfWidth, DerivedFunc::DoubleWidth)
+            | (DerivedFunc::DoubleWidth, DerivedFunc::HalfWidth)
+            | (DerivedFunc::HalfVector, DerivedFunc::DoubleVector)
+            | (DerivedFunc::DoubleVector, DerivedFunc::HalfVector) => {
+                // Cancelling bijective transformations. This doesn't hide any overflow issues
+                // since derived type sets are checked upon derivaion, and base typesets are only
+                // allowed to shrink.
+                return canonicalize_derivations(base_base_tv.clone());
+            }
+            (DerivedFunc::HalfWidth, DerivedFunc::HalfVector)
+            | (DerivedFunc::HalfWidth, DerivedFunc::DoubleVector)
+            | (DerivedFunc::DoubleWidth, DerivedFunc::DoubleVector)
+            | (DerivedFunc::DoubleWidth, DerivedFunc::HalfVector) => {
+                // Arbitrarily put WIDTH derivations before VECTOR derivations, since they commute.
+                return canonicalize_derivations(
+                    base_base_tv
+                        .derived(derived_func)
+                        .derived(base_base.derived_func),
+                );
+            }
+            _ => {}
+        };
+    }
+
+    canonicalize_derivations(base.type_var.clone()).derived(derived_func)
+}
+
+/// Given typevars tv1 and tv2 (which could be derived from one another), constrain their typesets
+/// to be the same. When one is derived from the other, repeat the constrain process until
+/// a fixed point is reached.
+fn constrain_fixpoint(tv1: &TypeVar, tv2: &TypeVar) {
+    loop {
+        let old_tv1_ts = tv1.get_typeset().clone();
+        tv2.constrain_types(tv1.clone());
+        if tv1.get_typeset() == old_tv1_ts {
+            break;
+        }
+    }
+
+    let old_tv2_ts = tv2.get_typeset().clone();
+    tv1.constrain_types(tv2.clone());
+    // The above loop should ensure that all reference cycles have been handled.
+    assert!(old_tv2_ts == tv2.get_typeset());
+}
+
+/// Unify tv1 and tv2 in the given type environment. tv1 must have a rank greater or equal to tv2's
+/// one, modulo commutations.
+fn unify(tv1: &TypeVar, tv2: &TypeVar, type_env: &mut TypeEnvironment) -> Result<(), String> {
+    let tv1 = canonicalize_derivations(type_env.get_equivalent(tv1));
+    let tv2 = canonicalize_derivations(type_env.get_equivalent(tv2));
+
+    if tv1 == tv2 {
+        // Already unified.
+        return Ok(());
+    }
+
+    if type_env.rank(&tv2) < type_env.rank(&tv1) {
+        // Make sure tv1 always has the smallest rank, since real variables have the higher rank
+        // and we want them to be the canonical representatives of their equivalency classes.
+        return unify(&tv2, &tv1, type_env);
+    }
+
+    constrain_fixpoint(&tv1, &tv2);
+
+    if tv1.get_typeset().size() == 0 || tv2.get_typeset().size() == 0 {
+        return Err(format!(
+            "Error: empty type created when unifying {} and {}",
+            tv1.name, tv2.name
+        ));
+    }
+
+    let base = match &tv1.base {
+        Some(base) => base,
+        None => {
+            type_env.record_equivalent(tv1, tv2);
+            return Ok(());
+        }
+    };
+
+    if let Some(inverse) = base.derived_func.inverse() {
+        return unify(&base.type_var, &tv2.derived(inverse), type_env);
+    }
+
+    type_env.add_constraint(Constraint::Eq(tv1, tv2));
+    Ok(())
+}
+
+/// Perform type inference on one Def in the current type environment and return an updated type
+/// environment or error.
+///
+/// At a high level this works by creating fresh copies of each formal type var in the Def's
+/// instruction's signature, and unifying the formal typevar with the corresponding actual typevar.
+fn infer_definition(
+    def: &Def,
+    var_pool: &mut VarPool,
+    type_env: TypeEnvironment,
+    last_type_index: &mut usize,
+) -> Result<TypeEnvironment, String> {
+    let apply = &def.apply;
+    let inst = &apply.inst;
+
+    let mut type_env = type_env;
+    let free_formal_tvs = inst.all_typevars();
+
+    let mut original_to_own_typevar: HashMap<&TypeVar, TypeVar> = HashMap::new();
+    for &tv in &free_formal_tvs {
+        assert!(original_to_own_typevar
+            .insert(
+                tv,
+                TypeVar::copy_from(tv, format!("own_{}", last_type_index))
+            )
+            .is_none());
+        *last_type_index += 1;
+    }
+
+    // Update the mapping with any explicity bound type vars:
+    for (i, value_type) in apply.value_types.iter().enumerate() {
+        let singleton = TypeVar::new_singleton(value_type.clone());
+        assert!(original_to_own_typevar
+            .insert(free_formal_tvs[i], singleton)
+            .is_some());
+    }
+
+    // Get fresh copies for each typevar in the signature (both free and derived).
+    let mut formal_tvs = Vec::new();
+    formal_tvs.extend(inst.value_results.iter().map(|&i| {
+        substitute(
+            &original_to_own_typevar,
+            inst.operands_out[i].type_var().unwrap(),
+        )
+    }));
+    formal_tvs.extend(inst.value_opnums.iter().map(|&i| {
+        substitute(
+            &original_to_own_typevar,
+            inst.operands_in[i].type_var().unwrap(),
+        )
+    }));
+
+    // Get the list of actual vars.
+    let mut actual_vars = Vec::new();
+    actual_vars.extend(inst.value_results.iter().map(|&i| def.defined_vars[i]));
+    actual_vars.extend(
+        inst.value_opnums
+            .iter()
+            .map(|&i| apply.args[i].unwrap_var()),
+    );
+
+    // Get the list of the actual TypeVars.
+    let mut actual_tvs = Vec::new();
+    for var_index in actual_vars {
+        let var = var_pool.get_mut(var_index);
+        type_env.register(var_index, var);
+        actual_tvs.push(var.get_or_create_typevar());
+    }
+
+    // Make sure we start unifying with the control type variable first, by putting it at the
+    // front of both vectors.
+    if let Some(poly) = &inst.polymorphic_info {
+        let own_ctrl_tv = &original_to_own_typevar[&poly.ctrl_typevar];
+        let ctrl_index = formal_tvs.iter().position(|tv| tv == own_ctrl_tv).unwrap();
+        if ctrl_index != 0 {
+            formal_tvs.swap(0, ctrl_index);
+            actual_tvs.swap(0, ctrl_index);
+        }
+    }
+
+    // Unify each actual type variable with the corresponding formal type variable.
+    for (actual_tv, formal_tv) in actual_tvs.iter().zip(&formal_tvs) {
+        if let Err(msg) = unify(actual_tv, formal_tv, &mut type_env) {
+            return Err(format!(
+                "fail ti on {} <: {}: {}",
+                actual_tv.name, formal_tv.name, msg
+            ));
+        }
+    }
+
+    // Add any instruction specific constraints.
+    for constraint in &inst.constraints {
+        type_env.add_constraint(constraint.translate_with_map(&original_to_own_typevar));
+    }
+
+    Ok(type_env)
+}
+
+/// Perform type inference on an transformation. Return an updated type environment or error.
+pub fn infer_transform(
+    src: DefIndex,
+    dst: &Vec<DefIndex>,
+    def_pool: &DefPool,
+    var_pool: &mut VarPool,
+) -> Result<TypeEnvironment, String> {
+    let mut type_env = TypeEnvironment::new();
+    let mut last_type_index = 0;
+
+    // Execute type inference on the source pattern.
+    type_env = infer_definition(def_pool.get(src), var_pool, type_env, &mut last_type_index)
+        .map_err(|err| format!("In src pattern: {}", err))?;
+
+    // Collect the type sets once after applying the source patterm; we'll compare the typesets
+    // after we've also considered the destination pattern, and will emit supplementary InTypeset
+    // checks if they don't match.
+    let src_typesets = type_env
+        .vars
+        .iter()
+        .map(|&var_index| {
+            let var = var_pool.get_mut(var_index);
+            let tv = type_env.get_equivalent(&var.get_or_create_typevar());
+            (var_index, tv.get_typeset().clone())
+        })
+        .collect::<Vec<_>>();
+
+    // Execute type inference on the destination pattern.
+    for (i, &def_index) in dst.iter().enumerate() {
+        let def = def_pool.get(def_index);
+        type_env = infer_definition(def, var_pool, type_env, &mut last_type_index)
+            .map_err(|err| format!("line {}: {}", i, err))?;
+    }
+
+    for (var_index, src_typeset) in src_typesets {
+        let var = var_pool.get(var_index);
+        if !var.has_free_typevar() {
+            continue;
+        }
+        let tv = type_env.get_equivalent(&var.get_typevar().unwrap());
+        let new_typeset = tv.get_typeset();
+        assert!(
+            new_typeset.is_subset(&src_typeset),
+            "type sets can only get narrower"
+        );
+        if new_typeset != src_typeset {
+            type_env.add_constraint(Constraint::InTypeset(tv.clone(), new_typeset.clone()));
+        }
+    }
+
+    type_env.normalize(var_pool);
+
+    Ok(type_env.extract(var_pool))
+}
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/typevar.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/typevar.rs
@@ -1,9 +1,12 @@
-use std::collections::BTreeSet;
+use std::cell::RefCell;
+use std::collections::{BTreeSet, HashSet};
+use std::fmt;
+use std::hash;
 use std::iter::FromIterator;
 use std::ops;
 use std::rc::Rc;
 
 use crate::cdsl::types::{BVType, LaneType, SpecialType, ValueType};
 
 const MAX_LANES: u16 = 256;
 const MAX_BITS: u16 = 64;
@@ -22,35 +25,35 @@ pub struct TypeVarContent {
     pub name: String,
 
     /// Documentation string.
     pub doc: String,
 
     /// Type set associated to the type variable.
     /// This field must remain private; use `get_typeset()` or `get_raw_typeset()` to get the
     /// information you want.
-    type_set: Rc<TypeSet>,
+    type_set: TypeSet,
 
     pub base: Option<TypeVarParent>,
 }
 
 #[derive(Clone, Debug)]
 pub struct TypeVar {
-    content: Rc<TypeVarContent>,
+    content: Rc<RefCell<TypeVarContent>>,
 }
 
 impl TypeVar {
     pub fn new(name: impl Into<String>, doc: impl Into<String>, type_set: TypeSet) -> Self {
         Self {
-            content: Rc::new(TypeVarContent {
+            content: Rc::new(RefCell::new(TypeVarContent {
                 name: name.into(),
                 doc: doc.into(),
-                type_set: Rc::new(type_set),
+                type_set,
                 base: None,
-            }),
+            })),
         }
     }
 
     pub fn new_singleton(value_type: ValueType) -> Self {
         let (name, doc) = (value_type.to_string(), value_type.doc());
         let mut builder = TypeSetBuilder::new();
 
         let (scalar_type, num_lanes) = match value_type {
@@ -81,58 +84,75 @@ impl TypeVar {
             LaneType::BoolType(bool_type) => {
                 let bits = bool_type as RangeBound;
                 builder.bools(bits..bits)
             }
         };
         TypeVar::new(name, doc, builder.finish())
     }
 
-    /// Returns this typevar's type set, maybe computing it from the parent.
-    fn get_typeset(&self) -> Rc<TypeSet> {
-        // TODO Can this be done in a non-lazy way in derived() and we can remove this function and
-        // the one below?
-        match &self.content.base {
-            Some(base) => Rc::new(base.type_var.get_typeset().image(base.derived_func)),
-            None => self.content.type_set.clone(),
+    /// Get a fresh copy of self, named after `name`. Can only be called on non-derived typevars.
+    pub fn copy_from(other: &TypeVar, name: String) -> TypeVar {
+        assert!(
+            other.base.is_none(),
+            "copy_from() can only be called on non-derived type variables"
+        );
+        TypeVar {
+            content: Rc::new(RefCell::new(TypeVarContent {
+                name,
+                doc: "".into(),
+                type_set: other.type_set.clone(),
+                base: None,
+            })),
+        }
+    }
+
+    /// Returns the typeset for this TV. If the TV is derived, computes it recursively from the
+    /// derived function and the base's typeset.
+    /// Note this can't be done non-lazily in the constructor, because the TypeSet of the base may
+    /// change over time.
+    pub fn get_typeset(&self) -> TypeSet {
+        match &self.base {
+            Some(base) => base.type_var.get_typeset().image(base.derived_func),
+            None => self.type_set.clone(),
         }
     }
 
     /// Returns this typevar's type set, assuming this type var has no parent.
     pub fn get_raw_typeset(&self) -> &TypeSet {
-        assert_eq!(self.content.type_set, self.get_typeset());
-        &*self.content.type_set
+        assert_eq!(self.type_set, self.get_typeset());
+        &self.type_set
     }
 
     /// If the associated typeset has a single type return it. Otherwise return None.
     pub fn singleton_type(&self) -> Option<ValueType> {
         let type_set = self.get_typeset();
         if type_set.size() == 1 {
             Some(type_set.get_singleton())
         } else {
             None
         }
     }
 
     /// Get the free type variable controlling this one.
     pub fn free_typevar(&self) -> Option<TypeVar> {
-        match &self.content.base {
+        match &self.base {
             Some(base) => base.type_var.free_typevar(),
             None => {
                 match self.singleton_type() {
                     // A singleton type isn't a proper free variable.
                     Some(_) => None,
                     None => Some(self.clone()),
                 }
             }
         }
     }
 
     /// Create a type variable that is a function of another.
-    fn derived(&self, derived_func: DerivedFunc) -> TypeVar {
+    pub fn derived(&self, derived_func: DerivedFunc) -> TypeVar {
         let ts = self.get_typeset();
 
         // Safety checks to avoid over/underflows.
         assert!(ts.specials.len() == 0, "can't derive from special types");
         match derived_func {
             DerivedFunc::HalfWidth => {
                 assert!(
                     ts.ints.len() == 0 || *ts.ints.iter().min().unwrap() > 8,
@@ -174,25 +194,25 @@ impl TypeVar {
                 );
             }
             DerivedFunc::LaneOf | DerivedFunc::ToBitVec | DerivedFunc::AsBool => {
                 /* no particular assertions */
             }
         }
 
         return TypeVar {
-            content: Rc::new(TypeVarContent {
+            content: Rc::new(RefCell::new(TypeVarContent {
                 name: format!("{}({})", derived_func.name(), self.name),
                 doc: "".into(),
                 type_set: ts,
                 base: Some(TypeVarParent {
                     type_var: self.clone(),
                     derived_func,
                 }),
-            }),
+            })),
         };
     }
 
     pub fn lane_of(&self) -> TypeVar {
         return self.derived(DerivedFunc::LaneOf);
     }
     pub fn as_bool(&self) -> TypeVar {
         return self.derived(DerivedFunc::AsBool);
@@ -207,47 +227,115 @@ impl TypeVar {
         return self.derived(DerivedFunc::HalfVector);
     }
     pub fn double_vector(&self) -> TypeVar {
         return self.derived(DerivedFunc::DoubleVector);
     }
     pub fn to_bitvec(&self) -> TypeVar {
         return self.derived(DerivedFunc::ToBitVec);
     }
+
+    /// Constrain the range of types this variable can assume to a subset of those in the typeset
+    /// ts.
+    /// May mutate itself if it's not derived, or its parent if it is.
+    pub fn constrain_types_by_ts(&self, type_set: TypeSet) {
+        match &self.base {
+            Some(base) => {
+                base.type_var
+                    .constrain_types_by_ts(type_set.preimage(base.derived_func));
+            }
+            None => {
+                self.content
+                    .borrow_mut()
+                    .type_set
+                    .inplace_intersect_with(&type_set);
+            }
+        }
+    }
+
+    /// Constrain the range of types this variable can assume to a subset of those `other` can
+    /// assume.
+    /// May mutate itself if it's not derived, or its parent if it is.
+    pub fn constrain_types(&self, other: TypeVar) {
+        if self == &other {
+            return;
+        }
+        self.constrain_types_by_ts(other.get_typeset());
+    }
+
+    /// Get a Rust expression that computes the type of this type variable.
+    pub fn to_rust_code(&self) -> String {
+        match &self.base {
+            Some(base) => format!(
+                "{}.{}()",
+                base.type_var.to_rust_code(),
+                base.derived_func.name()
+            ),
+            None => {
+                if let Some(singleton) = self.singleton_type() {
+                    singleton.rust_name()
+                } else {
+                    self.name.clone()
+                }
+            }
+        }
+    }
 }
 
 impl Into<TypeVar> for &TypeVar {
     fn into(self) -> TypeVar {
         self.clone()
     }
 }
 impl Into<TypeVar> for ValueType {
     fn into(self) -> TypeVar {
         TypeVar::new_singleton(self)
     }
 }
 
+// Hash TypeVars by pointers.
+// There might be a better way to do this, but since TypeVar's content (namely TypeSet) can be
+// mutated, it makes sense to use pointer equality/hashing here.
+impl hash::Hash for TypeVar {
+    fn hash<H: hash::Hasher>(&self, h: &mut H) {
+        match &self.base {
+            Some(base) => {
+                base.type_var.hash(h);
+                base.derived_func.hash(h);
+            }
+            None => {
+                (&**self as *const TypeVarContent).hash(h);
+            }
+        }
+    }
+}
+
 impl PartialEq for TypeVar {
     fn eq(&self, other: &TypeVar) -> bool {
-        match (&self.content.base, &other.content.base) {
-            (Some(base1), Some(base2)) => base1.type_var.eq(&base2.type_var),
+        match (&self.base, &other.base) {
+            (Some(base1), Some(base2)) => {
+                base1.type_var.eq(&base2.type_var) && base1.derived_func == base2.derived_func
+            }
             (None, None) => Rc::ptr_eq(&self.content, &other.content),
             _ => false,
         }
     }
 }
 
+// Allow TypeVar as map keys, based on pointer equality (see also above PartialEq impl).
+impl Eq for TypeVar {}
+
 impl ops::Deref for TypeVar {
     type Target = TypeVarContent;
     fn deref(&self) -> &Self::Target {
-        &*self.content
+        unsafe { self.content.as_ptr().as_ref().unwrap() }
     }
 }
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq)]
 pub enum DerivedFunc {
     LaneOf,
     AsBool,
     HalfWidth,
     DoubleWidth,
     HalfVector,
     DoubleVector,
     ToBitVec,
@@ -260,19 +348,30 @@ impl DerivedFunc {
             DerivedFunc::AsBool => "as_bool",
             DerivedFunc::HalfWidth => "half_width",
             DerivedFunc::DoubleWidth => "double_width",
             DerivedFunc::HalfVector => "half_vector",
             DerivedFunc::DoubleVector => "double_vector",
             DerivedFunc::ToBitVec => "to_bitvec",
         }
     }
+
+    /// Returns the inverse function of this one, if it is a bijection.
+    pub fn inverse(&self) -> Option<DerivedFunc> {
+        match self {
+            DerivedFunc::HalfWidth => Some(DerivedFunc::DoubleWidth),
+            DerivedFunc::DoubleWidth => Some(DerivedFunc::HalfWidth),
+            DerivedFunc::HalfVector => Some(DerivedFunc::DoubleVector),
+            DerivedFunc::DoubleVector => Some(DerivedFunc::HalfVector),
+            _ => None,
+        }
+    }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Hash)]
 pub struct TypeVarParent {
     pub type_var: TypeVar,
     pub derived_func: DerivedFunc,
 }
 
 /// A set of types.
 ///
 /// We don't allow arbitrary subsets of types, but use a parametrized approach
@@ -296,17 +395,17 @@ type Range = ops::Range<RangeBound>;
 type NumSet = BTreeSet<RangeBound>;
 
 macro_rules! num_set {
     ($($expr:expr),*) => {
         NumSet::from_iter(vec![$($expr),*])
     };
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Clone, PartialEq, Eq, Hash)]
 pub struct TypeSet {
     pub lanes: NumSet,
     pub ints: NumSet,
     pub floats: NumSet,
     pub bools: NumSet,
     pub bitvecs: NumSet,
     pub specials: Vec<SpecialType>,
 }
@@ -326,17 +425,17 @@ impl TypeSet {
             floats,
             bools,
             bitvecs,
             specials,
         }
     }
 
     /// Return the number of concrete types represented by this typeset.
-    fn size(&self) -> usize {
+    pub fn size(&self) -> usize {
         self.lanes.len()
             * (self.ints.len() + self.floats.len() + self.bools.len() + self.bitvecs.len())
             + self.specials.len()
     }
 
     /// Return the image of self across the derived function func.
     fn image(&self, derived_func: DerivedFunc) -> TypeSet {
         match derived_func {
@@ -481,16 +580,185 @@ impl TypeSet {
     }
 
     /// Return the singleton type represented by self. Can only call on typesets containing 1 type.
     fn get_singleton(&self) -> ValueType {
         let mut types = self.concrete_types();
         assert_eq!(types.len(), 1);
         return types.remove(0);
     }
+
+    /// Return the inverse image of self across the derived function func.
+    fn preimage(&self, func: DerivedFunc) -> TypeSet {
+        if self.size() == 0 {
+            // The inverse of the empty set is itself.
+            return self.clone();
+        }
+
+        match func {
+            DerivedFunc::LaneOf => {
+                let mut copy = self.clone();
+                copy.bitvecs = NumSet::new();
+                copy.lanes =
+                    NumSet::from_iter((0..MAX_LANES.trailing_zeros() + 1).map(|i| u16::pow(2, i)));
+                copy
+            }
+            DerivedFunc::AsBool => {
+                let mut copy = self.clone();
+                copy.bitvecs = NumSet::new();
+                if self.bools.contains(&1) {
+                    copy.ints = NumSet::from_iter(vec![8, 16, 32, 64]);
+                    copy.floats = NumSet::from_iter(vec![32, 64]);
+                } else {
+                    copy.ints = &self.bools - &NumSet::from_iter(vec![1]);
+                    copy.floats = &self.bools & &NumSet::from_iter(vec![32, 64]);
+                    // If b1 is not in our typeset, than lanes=1 cannot be in the pre-image, as
+                    // as_bool() of scalars is always b1.
+                    copy.lanes = &self.lanes - &NumSet::from_iter(vec![1]);
+                }
+                copy
+            }
+            DerivedFunc::HalfWidth => self.double_width(),
+            DerivedFunc::DoubleWidth => self.half_width(),
+            DerivedFunc::HalfVector => self.double_vector(),
+            DerivedFunc::DoubleVector => self.half_vector(),
+            DerivedFunc::ToBitVec => {
+                let all_lanes = range_to_set(Some(1..MAX_LANES));
+                let all_ints = range_to_set(Some(8..MAX_BITS));
+                let all_floats = range_to_set(Some(32..64));
+                let all_bools = range_to_set(Some(1..MAX_BITS));
+
+                let mut lanes = range_to_set(Some(1..MAX_LANES));
+                let mut ints = range_to_set(Some(8..MAX_BITS));
+                let mut floats = range_to_set(Some(32..64));
+                let mut bools = range_to_set(Some(1..MAX_BITS));
+
+                for &l in &all_lanes {
+                    for &i in &all_ints {
+                        if self.bitvecs.contains(&(i * l)) {
+                            lanes.insert(l);
+                            ints.insert(i);
+                        }
+                    }
+                    for &f in &all_floats {
+                        if self.bitvecs.contains(&(f * l)) {
+                            lanes.insert(l);
+                            floats.insert(f);
+                        }
+                    }
+                    for &b in &all_bools {
+                        if self.bitvecs.contains(&(b * l)) {
+                            lanes.insert(l);
+                            bools.insert(b);
+                        }
+                    }
+                }
+
+                let bitvecs = NumSet::new();
+                let specials = Vec::new();
+                TypeSet::new(lanes, ints, floats, bools, bitvecs, specials)
+            }
+        }
+    }
+
+    pub fn inplace_intersect_with(&mut self, other: &TypeSet) {
+        self.lanes = &self.lanes & &other.lanes;
+        self.ints = &self.ints & &other.ints;
+        self.floats = &self.floats & &other.floats;
+        self.bools = &self.bools & &other.bools;
+        self.bitvecs = &self.bitvecs & &other.bitvecs;
+
+        let mut new_specials = Vec::new();
+        for spec in &self.specials {
+            if let Some(spec) = other.specials.iter().find(|&other_spec| other_spec == spec) {
+                new_specials.push(*spec);
+            }
+        }
+        self.specials = new_specials;
+    }
+
+    pub fn is_subset(&self, other: &TypeSet) -> bool {
+        self.lanes.is_subset(&other.lanes)
+            && self.ints.is_subset(&other.ints)
+            && self.floats.is_subset(&other.floats)
+            && self.bools.is_subset(&other.bools)
+            && self.bitvecs.is_subset(&other.bitvecs)
+            && {
+                let specials: HashSet<SpecialType> = HashSet::from_iter(self.specials.clone());
+                let other_specials = HashSet::from_iter(other.specials.clone());
+                specials.is_subset(&other_specials)
+            }
+    }
+
+    pub fn is_wider_or_equal(&self, other: &TypeSet) -> bool {
+        set_wider_or_equal(&self.ints, &other.ints)
+            && set_wider_or_equal(&self.floats, &other.floats)
+            && set_wider_or_equal(&self.bools, &other.bools)
+    }
+
+    pub fn is_narrower(&self, other: &TypeSet) -> bool {
+        set_narrower(&self.ints, &other.ints)
+            && set_narrower(&self.floats, &other.floats)
+            && set_narrower(&self.bools, &other.bools)
+    }
+}
+
+fn set_wider_or_equal(s1: &NumSet, s2: &NumSet) -> bool {
+    s1.len() > 0 && s2.len() > 0 && s1.iter().min() >= s2.iter().max()
+}
+
+fn set_narrower(s1: &NumSet, s2: &NumSet) -> bool {
+    s1.len() > 0 && s2.len() > 0 && s1.iter().min() < s2.iter().max()
+}
+
+impl fmt::Debug for TypeSet {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(fmt, "TypeSet(")?;
+
+        let mut subsets = Vec::new();
+        if !self.lanes.is_empty() {
+            subsets.push(format!(
+                "lanes={{{}}}",
+                Vec::from_iter(self.lanes.iter().map(|x| x.to_string())).join(", ")
+            ));
+        }
+        if !self.ints.is_empty() {
+            subsets.push(format!(
+                "ints={{{}}}",
+                Vec::from_iter(self.ints.iter().map(|x| x.to_string())).join(", ")
+            ));
+        }
+        if !self.floats.is_empty() {
+            subsets.push(format!(
+                "floats={{{}}}",
+                Vec::from_iter(self.floats.iter().map(|x| x.to_string())).join(", ")
+            ));
+        }
+        if !self.bools.is_empty() {
+            subsets.push(format!(
+                "bools={{{}}}",
+                Vec::from_iter(self.bools.iter().map(|x| x.to_string())).join(", ")
+            ));
+        }
+        if !self.bitvecs.is_empty() {
+            subsets.push(format!(
+                "bitvecs={{{}}}",
+                Vec::from_iter(self.bitvecs.iter().map(|x| x.to_string())).join(", ")
+            ));
+        }
+        if !self.specials.is_empty() {
+            subsets.push(format!(
+                "specials={{{}}}",
+                Vec::from_iter(self.specials.iter().map(|x| x.to_string())).join(", ")
+            ));
+        }
+
+        write!(fmt, "{})", subsets.join(", "))?;
+        Ok(())
+    }
 }
 
 pub struct TypeSetBuilder {
     ints: Interval,
     floats: Interval,
     bools: Interval,
     bitvecs: Interval,
     includes_scalars: bool,
@@ -558,16 +826,28 @@ impl TypeSetBuilder {
             range_to_set(self.simd_lanes.to_range(min_lanes..MAX_LANES, Some(1))),
             range_to_set(self.ints.to_range(8..MAX_BITS, None)),
             range_to_set(self.floats.to_range(32..64, None)),
             bools,
             range_to_set(self.bitvecs.to_range(1..MAX_BITVEC, None)),
             self.specials,
         )
     }
+
+    pub fn all() -> TypeSet {
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .floats(Interval::All)
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .bitvecs(Interval::All)
+            .specials(ValueType::all_special_types().collect())
+            .includes_scalars(true)
+            .finish()
+    }
 }
 
 #[derive(PartialEq)]
 pub enum Interval {
     None,
     All,
     Range(Range),
 }
@@ -801,16 +1081,146 @@ fn test_forward_images() {
     );
     assert_eq!(
         TypeSetBuilder::new().bools(32..64).finish().double_width(),
         TypeSetBuilder::new().bools(64..64).finish()
     );
 }
 
 #[test]
+fn test_backward_images() {
+    let empty_set = TypeSetBuilder::new().finish();
+
+    // LaneOf.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(1..1)
+            .ints(8..8)
+            .floats(32..32)
+            .finish()
+            .preimage(DerivedFunc::LaneOf),
+        TypeSetBuilder::new()
+            .simd_lanes(Interval::All)
+            .ints(8..8)
+            .floats(32..32)
+            .finish()
+    );
+    assert_eq!(empty_set.preimage(DerivedFunc::LaneOf), empty_set);
+
+    // AsBool.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(1..4)
+            .bools(1..64)
+            .finish()
+            .preimage(DerivedFunc::AsBool),
+        TypeSetBuilder::new()
+            .simd_lanes(1..4)
+            .ints(Interval::All)
+            .bools(Interval::All)
+            .floats(Interval::All)
+            .finish()
+    );
+
+    // Double vector.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(1..1)
+            .ints(8..8)
+            .finish()
+            .preimage(DerivedFunc::DoubleVector)
+            .size(),
+        0
+    );
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(1..16)
+            .ints(8..16)
+            .floats(32..32)
+            .finish()
+            .preimage(DerivedFunc::DoubleVector),
+        TypeSetBuilder::new()
+            .simd_lanes(1..8)
+            .ints(8..16)
+            .floats(32..32)
+            .finish(),
+    );
+
+    // Half vector.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(256..256)
+            .ints(8..8)
+            .finish()
+            .preimage(DerivedFunc::HalfVector)
+            .size(),
+        0
+    );
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(64..128)
+            .bools(1..32)
+            .finish()
+            .preimage(DerivedFunc::HalfVector),
+        TypeSetBuilder::new()
+            .simd_lanes(128..256)
+            .bools(1..32)
+            .finish(),
+    );
+
+    // Half width.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .ints(64..64)
+            .floats(64..64)
+            .bools(64..64)
+            .finish()
+            .preimage(DerivedFunc::HalfWidth)
+            .size(),
+        0
+    );
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(64..256)
+            .bools(1..64)
+            .finish()
+            .preimage(DerivedFunc::HalfWidth),
+        TypeSetBuilder::new()
+            .simd_lanes(64..256)
+            .bools(16..64)
+            .finish(),
+    );
+
+    // Double width.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .ints(8..8)
+            .floats(32..32)
+            .bools(1..8)
+            .finish()
+            .preimage(DerivedFunc::DoubleWidth)
+            .size(),
+        0
+    );
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(1..16)
+            .ints(8..16)
+            .floats(32..64)
+            .finish()
+            .preimage(DerivedFunc::DoubleWidth),
+        TypeSetBuilder::new()
+            .simd_lanes(1..16)
+            .ints(8..8)
+            .floats(32..32)
+            .finish()
+    );
+}
+
+#[test]
 #[should_panic]
 fn test_typeset_singleton_panic_nonsingleton_types() {
     TypeSetBuilder::new()
         .ints(8..8)
         .floats(32..32)
         .finish()
         .get_singleton();
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/xform.rs
@@ -0,0 +1,416 @@
+use crate::cdsl::ast::{
+    Apply, DefIndex, DefPool, DummyDef, DummyExpr, Expr, PatternPosition, VarIndex, VarPool,
+};
+use crate::cdsl::inst::Instruction;
+use crate::cdsl::type_inference::{infer_transform, TypeEnvironment};
+use crate::cdsl::typevar::TypeVar;
+
+use cranelift_entity::{entity_impl, PrimaryMap};
+
+use std::collections::{HashMap, HashSet};
+use std::iter::FromIterator;
+
+/// An instruction transformation consists of a source and destination pattern.
+///
+/// Patterns are expressed in *register transfer language* as tuples of Def or Expr nodes. A
+/// pattern may optionally have a sequence of TypeConstraints, that additionally limit the set of
+/// cases when it applies.
+///
+/// The source pattern can contain only a single instruction.
+pub struct Transform {
+    pub src: DefIndex,
+    pub dst: Vec<DefIndex>,
+    pub var_pool: VarPool,
+    pub def_pool: DefPool,
+    pub type_env: TypeEnvironment,
+}
+
+type SymbolTable = HashMap<&'static str, VarIndex>;
+
+impl Transform {
+    fn new(src: DummyDef, dst: Vec<DummyDef>) -> Self {
+        let mut var_pool = VarPool::new();
+        let mut def_pool = DefPool::new();
+
+        let mut input_vars: Vec<VarIndex> = Vec::new();
+        let mut defined_vars: Vec<VarIndex> = Vec::new();
+
+        // Maps variable names to our own Var copies.
+        let mut symbol_table: SymbolTable = SymbolTable::new();
+
+        // Rewrite variables in src and dst using our own copies.
+        let src = rewrite_def_list(
+            PatternPosition::Source,
+            vec![src],
+            &mut symbol_table,
+            &mut input_vars,
+            &mut defined_vars,
+            &mut var_pool,
+            &mut def_pool,
+        )[0];
+
+        let num_src_inputs = input_vars.len();
+
+        let dst = rewrite_def_list(
+            PatternPosition::Destination,
+            dst,
+            &mut symbol_table,
+            &mut input_vars,
+            &mut defined_vars,
+            &mut var_pool,
+            &mut def_pool,
+        );
+
+        // Sanity checks.
+        for &var_index in &input_vars {
+            assert!(
+                var_pool.get(var_index).is_input(),
+                format!("'{:?}' used as both input and def", var_pool.get(var_index))
+            );
+        }
+        assert!(
+            input_vars.len() == num_src_inputs,
+            format!(
+                "extra input vars in dst pattern: {:?}",
+                input_vars
+                    .iter()
+                    .map(|&i| var_pool.get(i))
+                    .skip(num_src_inputs)
+                    .collect::<Vec<_>>()
+            )
+        );
+
+        // Perform type inference and cleanup.
+        let type_env = infer_transform(src, &dst, &def_pool, &mut var_pool).unwrap();
+
+        // Sanity check: the set of inferred free type variables should be a subset of the type
+        // variables corresponding to Vars appearing in the source pattern.
+        {
+            let free_typevars: HashSet<TypeVar> =
+                HashSet::from_iter(type_env.free_typevars(&mut var_pool));
+            let src_tvs = HashSet::from_iter(
+                input_vars
+                    .clone()
+                    .iter()
+                    .chain(
+                        defined_vars
+                            .iter()
+                            .filter(|&&var_index| !var_pool.get(var_index).is_temp()),
+                    )
+                    .map(|&var_index| var_pool.get(var_index).get_typevar())
+                    .filter(|maybe_var| maybe_var.is_some())
+                    .map(|var| var.unwrap()),
+            );
+            if !free_typevars.is_subset(&src_tvs) {
+                let missing_tvs = (&free_typevars - &src_tvs)
+                    .iter()
+                    .map(|tv| tv.name.clone())
+                    .collect::<Vec<_>>()
+                    .join(", ");
+                panic!("Some free vars don't appear in src: {}", missing_tvs);
+            }
+        }
+
+        for &var_index in input_vars.iter().chain(defined_vars.iter()) {
+            let var = var_pool.get_mut(var_index);
+            let canon_tv = type_env.get_equivalent(&var.get_or_create_typevar());
+            var.set_typevar(canon_tv);
+        }
+
+        Self {
+            src,
+            dst,
+            var_pool,
+            def_pool,
+            type_env,
+        }
+    }
+
+    fn verify_legalize(&self) {
+        let def = self.def_pool.get(self.src);
+        for &var_index in def.defined_vars.iter() {
+            let defined_var = self.var_pool.get(var_index);
+            assert!(
+                defined_var.is_output(),
+                format!("{:?} not defined in the destination pattern", defined_var)
+            );
+        }
+    }
+}
+
+/// Given a list of symbols defined in a Def, rewrite them to local symbols. Yield the new locals.
+fn rewrite_defined_vars(
+    position: PatternPosition,
+    dummy_def: &DummyDef,
+    def_index: DefIndex,
+    symbol_table: &mut SymbolTable,
+    defined_vars: &mut Vec<VarIndex>,
+    var_pool: &mut VarPool,
+) -> Vec<VarIndex> {
+    let mut new_defined_vars = Vec::new();
+    for var in &dummy_def.defined_vars {
+        let own_var = match symbol_table.get(var.name) {
+            Some(&existing_var) => existing_var,
+            None => {
+                // Materialize the variable.
+                let new_var = var_pool.create(var.name);
+                symbol_table.insert(var.name, new_var);
+                defined_vars.push(new_var);
+                new_var
+            }
+        };
+        var_pool.get_mut(own_var).set_def(position, def_index);
+        new_defined_vars.push(own_var);
+    }
+    new_defined_vars
+}
+
+/// Find all uses of variables in `expr` and replace them with our own local symbols.
+fn rewrite_expr(
+    position: PatternPosition,
+    dummy_expr: DummyExpr,
+    symbol_table: &mut SymbolTable,
+    input_vars: &mut Vec<VarIndex>,
+    var_pool: &mut VarPool,
+) -> Apply {
+    let (apply_target, dummy_args) = if let DummyExpr::Apply(apply_target, dummy_args) = dummy_expr
+    {
+        (apply_target, dummy_args)
+    } else {
+        panic!("we only rewrite apply expressions");
+    };
+
+    assert_eq!(
+        apply_target.inst().operands_in.len(),
+        dummy_args.len(),
+        "number of arguments in instruction is incorrect"
+    );
+
+    let mut args = Vec::new();
+    for (i, arg) in dummy_args.into_iter().enumerate() {
+        match arg {
+            DummyExpr::Var(var) => {
+                let own_var = match symbol_table.get(var.name) {
+                    Some(&own_var) => {
+                        let var = var_pool.get(own_var);
+                        assert!(
+                            var.is_input() || var.get_def(position).is_some(),
+                            format!("{:?} used as both input and def", var)
+                        );
+                        own_var
+                    }
+                    None => {
+                        // First time we're using this variable.
+                        let own_var = var_pool.create(var.name);
+                        symbol_table.insert(var.name, own_var);
+                        input_vars.push(own_var);
+                        own_var
+                    }
+                };
+                args.push(Expr::Var(own_var));
+            }
+            DummyExpr::Literal(literal) => {
+                assert!(!apply_target.inst().operands_in[i].is_value());
+                args.push(Expr::Literal(literal));
+            }
+            DummyExpr::Apply(..) => {
+                panic!("Recursive apply is not allowed.");
+            }
+        }
+    }
+
+    Apply::new(apply_target, args)
+}
+
+fn rewrite_def_list(
+    position: PatternPosition,
+    dummy_defs: Vec<DummyDef>,
+    symbol_table: &mut SymbolTable,
+    input_vars: &mut Vec<VarIndex>,
+    defined_vars: &mut Vec<VarIndex>,
+    var_pool: &mut VarPool,
+    def_pool: &mut DefPool,
+) -> Vec<DefIndex> {
+    let mut new_defs = Vec::new();
+    for dummy_def in dummy_defs {
+        let def_index = def_pool.next_index();
+
+        let new_defined_vars = rewrite_defined_vars(
+            position,
+            &dummy_def,
+            def_index,
+            symbol_table,
+            defined_vars,
+            var_pool,
+        );
+        let new_apply = rewrite_expr(position, dummy_def.expr, symbol_table, input_vars, var_pool);
+
+        assert!(
+            def_pool.next_index() == def_index,
+            "shouldn't have created new defs in the meanwhile"
+        );
+        assert_eq!(
+            new_apply.inst.value_results.len(),
+            new_defined_vars.len(),
+            "number of Var results in instruction is incorrect"
+        );
+
+        new_defs.push(def_pool.create(new_apply, new_defined_vars));
+    }
+    new_defs
+}
+
+/// A group of related transformations.
+pub struct TransformGroup {
+    pub name: &'static str,
+    pub doc: &'static str,
+    pub chain_with: Option<TransformGroupIndex>,
+    pub isa_name: Option<&'static str>,
+    pub id: TransformGroupIndex,
+
+    /// Maps Instruction camel_case names to custom legalization functions names.
+    pub custom_legalizes: HashMap<String, &'static str>,
+    pub transforms: Vec<Transform>,
+}
+
+impl TransformGroup {
+    pub fn rust_name(&self) -> String {
+        match self.isa_name {
+            Some(_) => {
+                // This is a function in the same module as the LEGALIZE_ACTIONS table referring to
+                // it.
+                self.name.to_string()
+            }
+            None => format!("crate::legalizer::{}", self.name),
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct TransformGroupIndex(u32);
+entity_impl!(TransformGroupIndex);
+
+pub struct TransformGroupBuilder {
+    name: &'static str,
+    doc: &'static str,
+    chain_with: Option<TransformGroupIndex>,
+    isa_name: Option<&'static str>,
+    pub custom_legalizes: HashMap<String, &'static str>,
+    pub transforms: Vec<Transform>,
+}
+
+impl TransformGroupBuilder {
+    pub fn new(name: &'static str, doc: &'static str) -> Self {
+        Self {
+            name,
+            doc,
+            chain_with: None,
+            isa_name: None,
+            custom_legalizes: HashMap::new(),
+            transforms: Vec::new(),
+        }
+    }
+
+    pub fn chain_with(mut self, next_id: TransformGroupIndex) -> Self {
+        assert!(self.chain_with.is_none());
+        self.chain_with = Some(next_id);
+        self
+    }
+
+    pub fn isa(mut self, isa_name: &'static str) -> Self {
+        assert!(self.isa_name.is_none());
+        self.isa_name = Some(isa_name);
+        self
+    }
+
+    /// Add a custom legalization action for `inst`.
+    ///
+    /// The `func_name` parameter is the fully qualified name of a Rust function which takes the
+    /// same arguments as the `isa::Legalize` actions.
+    ///
+    /// The custom function will be called to legalize `inst` and any return value is ignored.
+    pub fn custom_legalize(&mut self, inst: &Instruction, func_name: &'static str) {
+        assert!(
+            self.custom_legalizes
+                .insert(inst.camel_name.clone(), func_name)
+                .is_none(),
+            format!(
+                "custom legalization action for {} inserted twice",
+                inst.name
+            )
+        );
+    }
+
+    /// Add a legalization pattern to this group.
+    pub fn legalize(&mut self, src: DummyDef, dst: Vec<DummyDef>) {
+        let transform = Transform::new(src, dst);
+        transform.verify_legalize();
+        self.transforms.push(transform);
+    }
+
+    pub fn finish_and_add_to(self, owner: &mut TransformGroups) -> TransformGroupIndex {
+        let next_id = owner.next_key();
+        owner.add(TransformGroup {
+            name: self.name,
+            doc: self.doc,
+            isa_name: self.isa_name,
+            id: next_id,
+            chain_with: self.chain_with,
+            custom_legalizes: self.custom_legalizes,
+            transforms: self.transforms,
+        })
+    }
+}
+
+pub struct TransformGroups {
+    groups: PrimaryMap<TransformGroupIndex, TransformGroup>,
+}
+
+impl TransformGroups {
+    pub fn new() -> Self {
+        Self {
+            groups: PrimaryMap::new(),
+        }
+    }
+    pub fn add(&mut self, new_group: TransformGroup) -> TransformGroupIndex {
+        for group in self.groups.values() {
+            assert!(
+                group.name != new_group.name,
+                format!("trying to insert {} for the second time", new_group.name)
+            );
+        }
+        self.groups.push(new_group)
+    }
+    pub fn get(&self, id: TransformGroupIndex) -> &TransformGroup {
+        &self.groups[id]
+    }
+    pub fn get_mut(&mut self, id: TransformGroupIndex) -> &mut TransformGroup {
+        self.groups.get_mut(id).unwrap()
+    }
+    fn next_key(&self) -> TransformGroupIndex {
+        self.groups.next_key()
+    }
+    pub fn by_name(&self, name: &'static str) -> &TransformGroup {
+        for group in self.groups.values() {
+            if group.name == name {
+                return group;
+            }
+        }
+        panic!(format!("transform group with name {} not found", name));
+    }
+}
+
+#[test]
+#[should_panic]
+fn test_double_custom_legalization() {
+    use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder};
+    use crate::cdsl::inst::InstructionBuilder;
+
+    let mut format = FormatRegistry::new();
+    format.insert(InstructionFormatBuilder::new("nullary"));
+    let dummy_inst = InstructionBuilder::new("dummy", "doc").finish(&format);
+
+    let mut transform_group = TransformGroupBuilder::new("test", "doc");
+    transform_group.custom_legalize(&dummy_inst, "custom 1");
+    transform_group.custom_legalize(&dummy_inst, "custom 2");
+}
--- a/third_party/rust/cranelift-codegen-meta/src/gen_inst.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/gen_inst.rs
@@ -660,17 +660,17 @@ fn typeset_to_string(ts: &TypeSet) -> St
     if ts.specials.len() > 0 {
         result += &format!(", specials=[{}]", iterable_to_string(&ts.specials));
     }
     result += ")";
     result
 }
 
 /// Generate the table of ValueTypeSets described by type_sets.
-fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
+pub fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
     if type_sets.len() == 0 {
         return;
     }
 
     fmt.comment("Table of value type sets.");
     assert!(type_sets.len() <= TYPESET_LIMIT, "Too many type sets!");
     fmtln!(
         fmt,
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/gen_legalizer.rs
@@ -0,0 +1,578 @@
+use crate::cdsl::ast::{Def, DefPool, VarPool};
+use crate::cdsl::formats::FormatRegistry;
+use crate::cdsl::isa::TargetIsa;
+use crate::cdsl::type_inference::Constraint;
+use crate::cdsl::typevar::{TypeSet, TypeVar};
+use crate::cdsl::xform::{Transform, TransformGroup, TransformGroups};
+
+use crate::error;
+use crate::gen_inst::gen_typesets_table;
+use crate::srcgen::Formatter;
+use crate::unique_table::UniqueTable;
+
+use std::collections::{HashMap, HashSet};
+use std::iter::FromIterator;
+
+/// Given a `Def` node, emit code that extracts all the instruction fields from
+/// `pos.func.dfg[iref]`.
+///
+/// Create local variables named after the `Var` instances in `node`.
+///
+/// Also create a local variable named `predicate` with the value of the evaluated instruction
+/// predicate, or `true` if the node has no predicate.
+fn unwrap_inst(
+    transform: &Transform,
+    format_registry: &FormatRegistry,
+    fmt: &mut Formatter,
+) -> bool {
+    let var_pool = &transform.var_pool;
+    let def_pool = &transform.def_pool;
+
+    let def = def_pool.get(transform.src);
+    let apply = &def.apply;
+    let inst = &apply.inst;
+    let iform = format_registry.get(inst.format);
+
+    fmt.comment(format!(
+        "Unwrap {}",
+        def.to_comment_string(&transform.var_pool)
+    ));
+
+    // Extract the Var arguments.
+    let arg_names = apply
+        .args
+        .iter()
+        .map(|arg| match arg.maybe_var() {
+            Some(var_index) => var_pool.get(var_index).name,
+            None => "_",
+        })
+        .collect::<Vec<_>>()
+        .join(", ");
+
+    fmtln!(
+        fmt,
+        "let ({}, predicate) = if let crate::ir::InstructionData::{} {{",
+        arg_names,
+        iform.name
+    );
+    fmt.indent(|fmt| {
+        // Fields are encoded directly.
+        for field in &iform.imm_fields {
+            fmtln!(fmt, "{},", field.member);
+        }
+
+        if iform.num_value_operands == 1 {
+            fmt.line("arg,");
+        } else if iform.has_value_list || iform.num_value_operands > 1 {
+            fmt.line("ref args,");
+        }
+
+        fmt.line("..");
+        fmt.outdented_line("} = pos.func.dfg[inst] {");
+        fmt.line("let func = &pos.func;");
+
+        if iform.has_value_list {
+            fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
+        } else if iform.num_value_operands == 1 {
+            fmt.line("let args = [arg];")
+        }
+
+        // Generate the values for the tuple.
+        fmt.line("(");
+        fmt.indent(|fmt| {
+            for (op_num, op) in inst.operands_in.iter().enumerate() {
+                if op.is_immediate() {
+                    let n = inst.imm_opnums.iter().position(|&i| i == op_num).unwrap();
+                    fmtln!(fmt, "{},", iform.imm_fields[n].member);
+                } else if op.is_value() {
+                    let n = inst.value_opnums.iter().position(|&i| i == op_num).unwrap();
+                    fmtln!(fmt, "func.dfg.resolve_aliases(args[{}]),", n);
+                }
+            }
+
+            // Evaluate the instruction predicate if any.
+            fmt.multi_line(
+                &apply
+                    .inst_predicate_with_ctrl_typevar(format_registry, var_pool)
+                    .rust_predicate(),
+            );
+        });
+        fmt.line(")");
+
+        fmt.outdented_line("} else {");
+        fmt.line(r#"unreachable!("bad instruction format")"#);
+    });
+    fmtln!(fmt, "};");
+
+    for &op_num in &inst.value_opnums {
+        let arg = &apply.args[op_num];
+        if let Some(var_index) = arg.maybe_var() {
+            let var = var_pool.get(var_index);
+            if var.has_free_typevar() {
+                fmtln!(
+                    fmt,
+                    "let typeof_{} = pos.func.dfg.value_type({});",
+                    var.name,
+                    var.name
+                );
+            }
+        }
+    }
+
+    // If the definition creates results, detach the values and place them in locals.
+    let mut replace_inst = false;
+    if def.defined_vars.len() > 0 {
+        if def.defined_vars
+            == def_pool
+                .get(var_pool.get(def.defined_vars[0]).dst_def.unwrap())
+                .defined_vars
+        {
+            // Special case: The instruction replacing node defines the exact same values.
+            fmt.comment(format!(
+                "Results handled by {}.",
+                def_pool
+                    .get(var_pool.get(def.defined_vars[0]).dst_def.unwrap())
+                    .to_comment_string(var_pool)
+            ));
+            replace_inst = true;
+        } else {
+            // Boring case: Detach the result values, capture them in locals.
+            for &var_index in &def.defined_vars {
+                fmtln!(fmt, "let {};", var_pool.get(var_index).name);
+            }
+
+            fmt.line("{");
+            fmt.indent(|fmt| {
+                fmt.line("let r = pos.func.dfg.inst_results(inst);");
+                for i in 0..def.defined_vars.len() {
+                    let var = var_pool.get(def.defined_vars[i]);
+                    fmtln!(fmt, "{} = r[{}];", var.name, i);
+                }
+            });
+            fmt.line("}");
+
+            for &var_index in &def.defined_vars {
+                let var = var_pool.get(var_index);
+                if var.has_free_typevar() {
+                    fmtln!(
+                        fmt,
+                        "let typeof_{} = pos.func.dfg.value_type({});",
+                        var.name,
+                        var.name
+                    );
+                }
+            }
+        }
+    }
+    replace_inst
+}
+
+fn build_derived_expr(tv: &TypeVar) -> String {
+    let base = match &tv.base {
+        Some(base) => base,
+        None => {
+            assert!(tv.name.starts_with("typeof_"));
+            return format!("Some({})", tv.name);
+        }
+    };
+    let base_expr = build_derived_expr(&base.type_var);
+    format!(
+        "{}.map(|t: crate::ir::Type| t.{}())",
+        base_expr,
+        base.derived_func.name()
+    )
+}
+
+/// Emit rust code for the given check.
+///
+/// The emitted code is a statement redefining the `predicate` variable like this:
+///     let predicate = predicate && ...
+fn emit_runtime_typecheck<'a, 'b>(
+    constraint: &'a Constraint,
+    type_sets: &mut UniqueTable<'a, TypeSet>,
+    fmt: &mut Formatter,
+) {
+    match constraint {
+        Constraint::InTypeset(tv, ts) => {
+            let ts_index = type_sets.add(&ts);
+            fmt.comment(format!(
+                "{} must belong to {:?}",
+                tv.name,
+                type_sets.get(ts_index)
+            ));
+            fmtln!(
+                fmt,
+                "let predicate = predicate && TYPE_SETS[{}].contains({});",
+                ts_index,
+                tv.name
+            );
+        }
+        Constraint::Eq(tv1, tv2) => {
+            fmtln!(
+                fmt,
+                "let predicate = predicate && match ({}, {}) {{",
+                build_derived_expr(tv1),
+                build_derived_expr(tv2)
+            );
+            fmt.indent(|fmt| {
+                fmt.line("(Some(a), Some(b)) => a == b,");
+                fmt.comment("On overflow, constraint doesn\'t apply");
+                fmt.line("_ => false,");
+            });
+            fmtln!(fmt, "};");
+        }
+        Constraint::WiderOrEq(tv1, tv2) => {
+            fmtln!(
+                fmt,
+                "let predicate = predicate && match ({}, {}) {{",
+                build_derived_expr(tv1),
+                build_derived_expr(tv2)
+            );
+            fmt.indent(|fmt| {
+                fmt.line("(Some(a), Some(b)) => a.wider_or_equal(b),");
+                fmt.comment("On overflow, constraint doesn\'t apply");
+                fmt.line("_ => false,");
+            });
+            fmtln!(fmt, "};");
+        }
+    }
+}
+
+/// Determine if `node` represents one of the value splitting instructions: `isplit` or `vsplit.
+/// These instructions are lowered specially by the `legalize::split` module.
+fn is_value_split(def: &Def) -> bool {
+    let name = def.apply.inst.name;
+    name == "isplit" || name == "vsplit"
+}
+
+fn emit_dst_inst(def: &Def, def_pool: &DefPool, var_pool: &VarPool, fmt: &mut Formatter) {
+    let defined_vars = {
+        let vars = def
+            .defined_vars
+            .iter()
+            .map(|&var_index| var_pool.get(var_index).name)
+            .collect::<Vec<_>>();
+        if vars.len() == 1 {
+            vars[0].to_string()
+        } else {
+            format!("({})", vars.join(", "))
+        }
+    };
+
+    if is_value_split(def) {
+        // Split instructions are not emitted with the builder, but by calling special functions in
+        // the `legalizer::split` module. These functions will eliminate concat-split patterns.
+        fmt.line("let curpos = pos.position();");
+        fmt.line("let srcloc = pos.srcloc();");
+        fmtln!(
+            fmt,
+            "let {} = split::{}(pos.func, cfg, curpos, srcloc, {});",
+            defined_vars,
+            def.apply.inst.snake_name(),
+            def.apply.args[0].to_rust_code(var_pool)
+        );
+        return;
+    }
+
+    if def.defined_vars.is_empty() {
+        // This node doesn't define any values, so just insert the new instruction.
+        fmtln!(
+            fmt,
+            "pos.ins().{};",
+            def.apply.rust_builder(&def.defined_vars, var_pool)
+        );
+        return;
+    }
+
+    if let Some(src_def0) = var_pool.get(def.defined_vars[0]).src_def {
+        if def.defined_vars == def_pool.get(src_def0).defined_vars {
+            // The replacement instruction defines the exact same values as the source pattern.
+            // Unwrapping would have left the results intact.  Replace the whole instruction.
+            fmtln!(
+                fmt,
+                "let {} = pos.func.dfg.replace(inst).{};",
+                defined_vars,
+                def.apply.rust_builder(&def.defined_vars, var_pool)
+            );
+
+            // We need to bump the cursor so following instructions are inserted *after* the
+            // replaced instruction.
+            fmt.line("if pos.current_inst() == Some(inst) {");
+            fmt.indent(|fmt| {
+                fmt.line("pos.next_inst();");
+            });
+            fmt.line("}");
+            return;
+        }
+    }
+
+    // Insert a new instruction.
+    let mut builder = format!("let {} = pos.ins()", defined_vars);
+
+    if def.defined_vars.len() == 1 && var_pool.get(def.defined_vars[0]).is_output() {
+        // Reuse the single source result value.
+        builder = format!(
+            "{}.with_result({})",
+            builder,
+            var_pool.get(def.defined_vars[0]).to_rust_code()
+        );
+    } else if def
+        .defined_vars
+        .iter()
+        .any(|&var_index| var_pool.get(var_index).is_output())
+    {
+        // There are more than one output values that can be reused.
+        let array = def
+            .defined_vars
+            .iter()
+            .map(|&var_index| {
+                let var = var_pool.get(var_index);
+                if var.is_output() {
+                    format!("Some({})", var.name)
+                } else {
+                    "None".into()
+                }
+            })
+            .collect::<Vec<_>>()
+            .join(", ");
+        builder = format!("{}.with_results([{}])", builder, array);
+    }
+
+    fmtln!(
+        fmt,
+        "{}.{};",
+        builder,
+        def.apply.rust_builder(&def.defined_vars, var_pool)
+    );
+}
+
+/// Emit code for `transform`, assuming that the opcode of transform's root instruction
+/// has already been matched.
+///
+/// `inst: Inst` is the variable to be replaced. It is pointed to by `pos: Cursor`.
+/// `dfg: DataFlowGraph` is available and mutable.
+fn gen_transform<'a>(
+    transform: &'a Transform,
+    format_registry: &FormatRegistry,
+    type_sets: &mut UniqueTable<'a, TypeSet>,
+    fmt: &mut Formatter,
+) {
+    // Unwrap the source instruction, create local variables for the input variables.
+    let replace_inst = unwrap_inst(&transform, format_registry, fmt);
+
+    // Emit any runtime checks; these will rebind `predicate` emitted by unwrap_inst().
+    for constraint in &transform.type_env.constraints {
+        emit_runtime_typecheck(constraint, type_sets, fmt);
+    }
+
+    // Guard the actual expansion by `predicate`.
+    fmt.line("if predicate {");
+    fmt.indent(|fmt| {
+        // If we're going to delete `inst`, we need to detach its results first so they can be
+        // reattached during pattern expansion.
+        if !replace_inst {
+            fmt.line("pos.func.dfg.clear_results(inst);");
+        }
+
+        // Emit the destination pattern.
+        for &def_index in &transform.dst {
+            emit_dst_inst(
+                transform.def_pool.get(def_index),
+                &transform.def_pool,
+                &transform.var_pool,
+                fmt,
+            );
+        }
+
+        // Delete the original instruction if we didn't have an opportunity to replace it.
+        if !replace_inst {
+            fmt.line("let removed = pos.remove_inst();");
+            fmt.line("debug_assert_eq!(removed, inst);");
+        }
+        fmt.line("return true;");
+    });
+    fmt.line("}");
+}
+
+fn gen_transform_group<'a>(
+    group: &'a TransformGroup,
+    format_registry: &FormatRegistry,
+    transform_groups: &TransformGroups,
+    type_sets: &mut UniqueTable<'a, TypeSet>,
+    fmt: &mut Formatter,
+) {
+    fmt.doc_comment(group.doc);
+    fmt.line("#[allow(unused_variables,unused_assignments,non_snake_case)]");
+
+    // Function arguments.
+    fmtln!(fmt, "pub fn {}(", group.name);
+    fmt.indent(|fmt| {
+        fmt.line("inst: crate::ir::Inst,");
+        fmt.line("func: &mut crate::ir::Function,");
+        fmt.line("cfg: &mut crate::flowgraph::ControlFlowGraph,");
+        fmt.line("isa: &crate::isa::TargetIsa,");
+    });
+    fmtln!(fmt, ") -> bool {");
+
+    // Function body.
+    fmt.indent(|fmt| {
+        fmt.line("use crate::ir::InstBuilder;");
+        fmt.line("use crate::cursor::{Cursor, FuncCursor};");
+        fmt.line("let mut pos = FuncCursor::new(func).at_inst(inst);");
+        fmt.line("pos.use_srcloc(inst);");
+
+        // Group the transforms by opcode so we can generate a big switch.
+        // Preserve ordering.
+        let mut inst_to_transforms = HashMap::new();
+        for transform in &group.transforms {
+            let def_index = transform.src;
+            let inst = &transform.def_pool.get(def_index).apply.inst;
+            inst_to_transforms
+                .entry(inst.camel_name.clone())
+                .or_insert(Vec::new())
+                .push(transform);
+        }
+
+        let mut sorted_inst_names = Vec::from_iter(inst_to_transforms.keys());
+        sorted_inst_names.sort();
+
+        fmt.line("{");
+        fmt.indent(|fmt| {
+            fmt.line("match pos.func.dfg[inst].opcode() {");
+            fmt.indent(|fmt| {
+                for camel_name in sorted_inst_names {
+                    fmtln!(fmt, "ir::Opcode::{} => {{", camel_name);
+                    fmt.indent(|fmt| {
+                        for transform in inst_to_transforms.get(camel_name).unwrap() {
+                            gen_transform(transform, format_registry, type_sets, fmt);
+                        }
+                    });
+                    fmtln!(fmt, "}");
+                    fmt.empty_line();
+                }
+
+                // Emit the custom transforms. The Rust compiler will complain about any overlap with
+                // the normal transforms.
+                for (inst_camel_name, func_name) in &group.custom_legalizes {
+                    fmtln!(fmt, "ir::Opcode::{} => {{", inst_camel_name);
+                    fmt.indent(|fmt| {
+                        fmtln!(fmt, "{}(inst, pos.func, cfg, isa);", func_name);
+                        fmt.line("return true;");
+                    });
+                    fmtln!(fmt, "}");
+                    fmt.empty_line();
+                }
+
+                // We'll assume there are uncovered opcodes.
+                fmt.line("_ => {},");
+            });
+            fmt.line("}");
+        });
+        fmt.line("}");
+
+        // If we fall through, nothing was expanded; call the chain if any.
+        match &group.chain_with {
+            Some(group_id) => fmtln!(
+                fmt,
+                "{}(inst, pos.func, cfg, isa)",
+                transform_groups.get(*group_id).rust_name()
+            ),
+            None => fmt.line("false"),
+        };
+    });
+    fmtln!(fmt, "}");
+    fmt.empty_line();
+}
+
+/// Generate legalization functions for `isa` and add any shared `TransformGroup`s
+/// encountered to `shared_groups`.
+///
+/// Generate `TYPE_SETS` and `LEGALIZE_ACTIONS` tables.
+fn gen_isa(
+    isa: &TargetIsa,
+    format_registry: &FormatRegistry,
+    transform_groups: &TransformGroups,
+    shared_group_names: &mut HashSet<&'static str>,
+    fmt: &mut Formatter,
+) {
+    let mut type_sets = UniqueTable::new();
+    for group_index in isa.transitive_transform_groups(transform_groups) {
+        let group = transform_groups.get(group_index);
+        match group.isa_name {
+            Some(isa_name) => {
+                assert!(
+                    isa_name == isa.name,
+                    "ISA-specific legalizations must be used by the same ISA"
+                );
+                gen_transform_group(
+                    group,
+                    format_registry,
+                    transform_groups,
+                    &mut type_sets,
+                    fmt,
+                );
+            }
+            None => {
+                shared_group_names.insert(group.name);
+            }
+        }
+    }
+
+    gen_typesets_table(&type_sets, fmt);
+
+    let direct_groups = isa.direct_transform_groups();
+    fmtln!(
+        fmt,
+        "pub static LEGALIZE_ACTIONS: [isa::Legalize; {}] = [",
+        direct_groups.len()
+    );
+    fmt.indent(|fmt| {
+        for group_index in direct_groups {
+            fmtln!(fmt, "{},", transform_groups.get(group_index).rust_name());
+        }
+    });
+    fmtln!(fmt, "];");
+}
+
+/// Generate the legalizer files.
+pub fn generate(
+    isas: &Vec<TargetIsa>,
+    format_registry: &FormatRegistry,
+    transform_groups: &TransformGroups,
+    filename_prefix: &str,
+    out_dir: &str,
+) -> Result<(), error::Error> {
+    let mut shared_group_names = HashSet::new();
+
+    for isa in isas {
+        let mut fmt = Formatter::new();
+        gen_isa(
+            isa,
+            format_registry,
+            transform_groups,
+            &mut shared_group_names,
+            &mut fmt,
+        );
+        fmt.update_file(format!("{}-{}.rs", filename_prefix, isa.name), out_dir)?;
+    }
+
+    // Generate shared legalize groups.
+    let mut fmt = Formatter::new();
+    let mut type_sets = UniqueTable::new();
+    let mut sorted_shared_group_names = Vec::from_iter(shared_group_names);
+    sorted_shared_group_names.sort();
+    for group_name in &sorted_shared_group_names {
+        let group = transform_groups.by_name(group_name);
+        gen_transform_group(
+            group,
+            format_registry,
+            transform_groups,
+            &mut type_sets,
+            &mut fmt,
+        );
+    }
+    gen_typesets_table(&type_sets, &mut fmt);
+    fmt.update_file(format!("{}r.rs", filename_prefix), out_dir)?;
+
+    Ok(())
+}
--- a/third_party/rust/cranelift-codegen-meta/src/gen_settings.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/gen_settings.rs
@@ -283,46 +283,36 @@ fn gen_descriptors(group: &SettingGroup,
             fmtln!(fmt, "detail::Descriptor {");
             fmt.indent(|fmt| {
                 fmtln!(fmt, "name: \"{}\",", preset.name);
                 fmtln!(fmt, "offset: {},", (idx as u8) * group.settings_size);
                 fmtln!(fmt, "detail: detail::Detail::Preset,");
             });
             fmtln!(fmt, "},");
 
-            descriptor_index_map.insert(SettingOrPreset::Preset(preset), idx);
+            let whole_idx = idx + group.settings.len();
+            descriptor_index_map.insert(SettingOrPreset::Preset(preset), whole_idx);
         }
     });
     fmtln!(fmt, "];");
 
     // Generate enumerators.
     fmtln!(fmt, "static ENUMERATORS: [&str; {}] = [", enum_table.len());
     fmt.indent(|fmt| {
         for enum_val in enum_table.iter() {
             fmtln!(fmt, "\"{}\",", enum_val);
         }
     });
     fmtln!(fmt, "];");
 
     // Generate hash table.
     let mut hash_entries: Vec<SettingOrPreset> = Vec::new();
-    hash_entries.extend(
-        group
-            .settings
-            .iter()
-            .map(|x| SettingOrPreset::Setting(x))
-            .collect::<Vec<SettingOrPreset>>(),
-    );
-    hash_entries.extend(
-        group
-            .presets
-            .iter()
-            .map(|x| SettingOrPreset::Preset(x))
-            .collect::<Vec<SettingOrPreset>>(),
-    );
+    hash_entries.extend(group.settings.iter().map(|x| SettingOrPreset::Setting(x)));
+    hash_entries.extend(group.presets.iter().map(|x| SettingOrPreset::Preset(x)));
+
     let hash_table = generate_table(&hash_entries, |entry| simple_hash(entry.name()));
     fmtln!(fmt, "static HASH_TABLE: [u16; {}] = [", hash_table.len());
     fmt.indent(|fmt| {
         for h in &hash_table {
             match *h {
                 Some(setting_or_preset) => fmtln!(
                     fmt,
                     "{},",
@@ -336,17 +326,17 @@ fn gen_descriptors(group: &SettingGroup,
         }
     });
     fmtln!(fmt, "];");
 
     // Generate presets.
     fmtln!(
         fmt,
         "static PRESETS: [(u8, u8); {}] = [",
-        group.presets.len()
+        group.presets.len() * (group.settings_size as usize)
     );
     fmt.indent(|fmt| {
         for preset in &group.presets {
             fmt.comment(preset.name);
             for (mask, value) in preset.layout(&group) {
                 fmtln!(fmt, "(0b{:08b}, 0b{:08b}),", mask, value);
             }
         }
--- a/third_party/rust/cranelift-codegen-meta/src/isa/arm32/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/arm32/mod.rs
@@ -1,12 +1,14 @@
+use crate::cdsl::cpu_modes::CpuMode;
 use crate::cdsl::inst::InstructionGroup;
 use crate::cdsl::isa::TargetIsa;
 use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
 use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
+
 use crate::shared::Definitions as SharedDefinitions;
 
 fn define_settings(_shared: &SettingGroup) -> SettingGroup {
     let setting = SettingGroupBuilder::new("arm32");
     setting.finish()
 }
 
 fn define_regs() -> IsaRegs {
@@ -47,10 +49,21 @@ fn define_regs() -> IsaRegs {
 }
 
 pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
     let settings = define_settings(&shared_defs.settings);
     let regs = define_regs();
 
     let inst_group = InstructionGroup::new("arm32", "arm32 specific instruction set");
 
-    TargetIsa::new("arm32", inst_group, settings, regs)
+    // CPU modes for 32-bit ARM and Thumb2.
+    let mut a32 = CpuMode::new("A32");
+    let mut t32 = CpuMode::new("T32");
+
+    // TODO refine these.
+    let narrow = shared_defs.transform_groups.by_name("narrow");
+    a32.legalize_default(narrow);
+    t32.legalize_default(narrow);
+
+    let cpu_modes = vec![a32, t32];
+
+    TargetIsa::new("arm32", inst_group, settings, regs, cpu_modes)
 }
--- a/third_party/rust/cranelift-codegen-meta/src/isa/arm64/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/arm64/mod.rs
@@ -1,12 +1,14 @@
+use crate::cdsl::cpu_modes::CpuMode;
 use crate::cdsl::inst::InstructionGroup;
 use crate::cdsl::isa::TargetIsa;
 use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
 use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
+
 use crate::shared::Definitions as SharedDefinitions;
 
 fn define_settings(_shared: &SettingGroup) -> SettingGroup {
     let setting = SettingGroupBuilder::new("arm64");
     setting.finish()
 }
 
 fn define_registers() -> IsaRegs {
@@ -43,10 +45,18 @@ fn define_registers() -> IsaRegs {
 }
 
 pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
     let settings = define_settings(&shared_defs.settings);
     let regs = define_registers();
 
     let inst_group = InstructionGroup::new("arm64", "arm64 specific instruction set");
 
-    TargetIsa::new("arm64", inst_group, settings, regs)
+    let mut a64 = CpuMode::new("A64");
+
+    // TODO refine these.
+    let narrow = shared_defs.transform_groups.by_name("narrow");
+    a64.legalize_default(narrow);
+
+    let cpu_modes = vec![a64];
+
+    TargetIsa::new("arm64", inst_group, settings, regs, cpu_modes)
 }
--- a/third_party/rust/cranelift-codegen-meta/src/isa/riscv/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/riscv/mod.rs
@@ -1,12 +1,16 @@
+use crate::cdsl::cpu_modes::CpuMode;
 use crate::cdsl::inst::InstructionGroup;
 use crate::cdsl::isa::TargetIsa;
 use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
 use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
+
+use crate::shared::types::Float::{F32, F64};
+use crate::shared::types::Int::{I32, I64};
 use crate::shared::Definitions as SharedDefinitions;
 
 fn define_settings(shared: &SettingGroup) -> SettingGroup {
     let mut setting = SettingGroupBuilder::new("riscv");
 
     let supports_m = setting.add_bool(
         "supports_m",
         "CPU supports the 'M' extension (mul/div)",
@@ -32,17 +36,17 @@ fn define_settings(shared: &SettingGroup
         "enable_m",
         "Enable the use of 'M' instructions if available",
         true,
     );
 
     setting.add_bool(
         "enable_e",
         "Enable the 'RV32E' instruction set with only 16 registers",
-        true,
+        false,
     );
 
     let shared_enable_atomics = shared.get_bool("enable_atomics");
     let shared_enable_float = shared.get_bool("enable_float");
     let shared_enable_simd = shared.get_bool("enable_simd");
 
     setting.add_predicate("use_m", predicate!(supports_m && enable_m));
     setting.add_predicate("use_a", predicate!(supports_a && shared_enable_atomics));
@@ -79,10 +83,31 @@ fn define_registers() -> IsaRegs {
 }
 
 pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
     let settings = define_settings(&shared_defs.settings);
     let regs = define_registers();
 
     let inst_group = InstructionGroup::new("riscv", "riscv specific instruction set");
 
-    TargetIsa::new("riscv", inst_group, settings, regs)
+    // CPU modes for 32-bit and 64-bit operation.
+    let mut rv_32 = CpuMode::new("RV32");
+    let mut rv_64 = CpuMode::new("RV64");
+
+    let expand = shared_defs.transform_groups.by_name("expand");
+    let narrow = shared_defs.transform_groups.by_name("narrow");
+    rv_32.legalize_monomorphic(expand);
+    rv_32.legalize_default(narrow);
+    rv_32.legalize_type(I32, expand);
+    rv_32.legalize_type(F32, expand);
+    rv_32.legalize_type(F64, expand);
+
+    rv_64.legalize_monomorphic(expand);
+    rv_64.legalize_default(narrow);
+    rv_64.legalize_type(I32, expand);
+    rv_64.legalize_type(I64, expand);
+    rv_64.legalize_type(F32, expand);
+    rv_64.legalize_type(F64, expand);
+
+    let cpu_modes = vec![rv_32, rv_64];
+
+    TargetIsa::new("riscv", inst_group, settings, regs, cpu_modes)
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/x86/legalize.rs
@@ -0,0 +1,293 @@
+use crate::cdsl::ast::{bind, var, ExprBuilder, Literal};
+use crate::cdsl::inst::InstructionGroup;
+use crate::cdsl::xform::TransformGroupBuilder;
+
+use crate::shared::types::Int::{I32, I64};
+use crate::shared::Definitions as SharedDefinitions;
+
+pub fn define(shared: &mut SharedDefinitions, x86_instructions: &InstructionGroup) {
+    let mut group = TransformGroupBuilder::new(
+        "x86_expand",
+        r#"
+    Legalize instructions by expansion.
+
+    Use x86-specific instructions if needed."#,
+    )
+    .isa("x86")
+    .chain_with(shared.transform_groups.by_name("expand_flags").id);
+
+    // List of instructions.
+    let insts = &shared.instructions;
+    let band = insts.by_name("band");
+    let bor = insts.by_name("bor");
+    let clz = insts.by_name("clz");
+    let ctz = insts.by_name("ctz");
+    let fcmp = insts.by_name("fcmp");
+    let fcvt_from_uint = insts.by_name("fcvt_from_uint");
+    let fcvt_to_sint = insts.by_name("fcvt_to_sint");
+    let fcvt_to_uint = insts.by_name("fcvt_to_uint");
+    let fcvt_to_sint_sat = insts.by_name("fcvt_to_sint_sat");
+    let fcvt_to_uint_sat = insts.by_name("fcvt_to_uint_sat");
+    let fmax = insts.by_name("fmax");
+    let fmin = insts.by_name("fmin");
+    let iadd = insts.by_name("iadd");
+    let iconst = insts.by_name("iconst");
+    let imul = insts.by_name("imul");
+    let isub = insts.by_name("isub");
+    let popcnt = insts.by_name("popcnt");
+    let sdiv = insts.by_name("sdiv");
+    let selectif = insts.by_name("selectif");
+    let smulhi = insts.by_name("smulhi");
+    let srem = insts.by_name("srem");
+    let udiv = insts.by_name("udiv");
+    let umulhi = insts.by_name("umulhi");
+    let ushr_imm = insts.by_name("ushr_imm");
+    let urem = insts.by_name("urem");
+
+    let x86_bsf = x86_instructions.by_name("x86_bsf");
+    let x86_bsr = x86_instructions.by_name("x86_bsr");
+    let x86_umulx = x86_instructions.by_name("x86_umulx");
+    let x86_smulx = x86_instructions.by_name("x86_smulx");
+
+    // List of immediates.
+    let floatcc = shared.operand_kinds.by_name("floatcc");
+    let imm64 = shared.operand_kinds.by_name("imm64");
+    let intcc = shared.operand_kinds.by_name("intcc");
+
+    // Division and remainder.
+    //
+    // The srem expansion requires custom code because srem INT_MIN, -1 is not
+    // allowed to trap. The other ops need to check avoid_div_traps.
+    group.custom_legalize(sdiv, "expand_sdivrem");
+    group.custom_legalize(srem, "expand_sdivrem");
+    group.custom_legalize(udiv, "expand_udivrem");
+    group.custom_legalize(urem, "expand_udivrem");
+
+    // Double length (widening) multiplication.
+    let a = var("a");
+    let x = var("x");
+    let y = var("y");
+    let a1 = var("a1");
+    let a2 = var("a2");
+    let res_lo = var("res_lo");
+    let res_hi = var("res_hi");
+
+    group.legalize(
+        def!(res_hi = umulhi(x, y)),
+        vec![def!((res_lo, res_hi) = x86_umulx(x, y))],
+    );
+
+    group.legalize(
+        def!(res_hi = smulhi(x, y)),
+        vec![def!((res_lo, res_hi) = x86_smulx(x, y))],
+    );
+
+    // Floating point condition codes.
+    //
+    // The 8 condition codes in `supported_floatccs` are directly supported by a
+    // `ucomiss` or `ucomisd` instruction. The remaining codes need legalization
+    // patterns.
+
+    let floatcc_eq = Literal::enumerator_for(floatcc, "eq");
+    let floatcc_ord = Literal::enumerator_for(floatcc, "ord");
+    let floatcc_ueq = Literal::enumerator_for(floatcc, "ueq");
+    let floatcc_ne = Literal::enumerator_for(floatcc, "ne");
+    let floatcc_uno = Literal::enumerator_for(floatcc, "uno");
+    let floatcc_one = Literal::enumerator_for(floatcc, "one");
+
+    // Equality needs an explicit `ord` test which checks the parity bit.
+    group.legalize(
+        def!(a = fcmp(floatcc_eq, x, y)),
+        vec![
+            def!(a1 = fcmp(floatcc_ord, x, y)),
+            def!(a2 = fcmp(floatcc_ueq, x, y)),
+            def!(a = band(a1, a2)),
+        ],
+    );
+    group.legalize(
+        def!(a = fcmp(floatcc_ne, x, y)),
+        vec![
+            def!(a1 = fcmp(floatcc_uno, x, y)),
+            def!(a2 = fcmp(floatcc_one, x, y)),
+            def!(a = bor(a1, a2)),
+        ],
+    );
+
+    let floatcc_lt = &Literal::enumerator_for(floatcc, "lt");
+    let floatcc_gt = &Literal::enumerator_for(floatcc, "gt");
+    let floatcc_le = &Literal::enumerator_for(floatcc, "le");
+    let floatcc_ge = &Literal::enumerator_for(floatcc, "ge");
+    let floatcc_ugt = &Literal::enumerator_for(floatcc, "ugt");
+    let floatcc_ult = &Literal::enumerator_for(floatcc, "ult");
+    let floatcc_uge = &Literal::enumerator_for(floatcc, "uge");
+    let floatcc_ule = &Literal::enumerator_for(floatcc, "ule");
+
+    // Inequalities that need to be reversed.
+    for &(cc, rev_cc) in &[
+        (floatcc_lt, floatcc_gt),
+        (floatcc_le, floatcc_ge),
+        (floatcc_ugt, floatcc_ult),
+        (floatcc_uge, floatcc_ule),
+    ] {
+        group.legalize(def!(a = fcmp(cc, x, y)), vec![def!(a = fcmp(rev_cc, y, x))]);
+    }
+
+    // We need to modify the CFG for min/max legalization.
+    group.custom_legalize(fmin, "expand_minmax");
+    group.custom_legalize(fmax, "expand_minmax");
+
+    // Conversions from unsigned need special handling.
+    group.custom_legalize(fcvt_from_uint, "expand_fcvt_from_uint");
+    // Conversions from float to int can trap and modify the control flow graph.
+    group.custom_legalize(fcvt_to_sint, "expand_fcvt_to_sint");
+    group.custom_legalize(fcvt_to_uint, "expand_fcvt_to_uint");
+    group.custom_legalize(fcvt_to_sint_sat, "expand_fcvt_to_sint_sat");
+    group.custom_legalize(fcvt_to_uint_sat, "expand_fcvt_to_uint_sat");
+
+    // Count leading and trailing zeroes, for baseline x86_64
+    let c_minus_one = var("c_minus_one");
+    let c_thirty_one = var("c_thirty_one");
+    let c_thirty_two = var("c_thirty_two");
+    let c_sixty_three = var("c_sixty_three");
+    let c_sixty_four = var("c_sixty_four");
+    let index1 = var("index1");
+    let r2flags = var("r2flags");
+    let index2 = var("index2");
+
+    let intcc_eq = Literal::enumerator_for(intcc, "eq");
+    let imm64_minus_one = Literal::constant(imm64, -1);
+    let imm64_63 = Literal::constant(imm64, 63);
+    group.legalize(
+        def!(a = clz.I64(x)),
+        vec![
+            def!(c_minus_one = iconst(imm64_minus_one)),
+            def!(c_sixty_three = iconst(imm64_63)),
+            def!((index1, r2flags) = x86_bsr(x)),
+            def!(index2 = selectif(intcc_eq, r2flags, c_minus_one, index1)),
+            def!(a = isub(c_sixty_three, index2)),
+        ],
+    );
+
+    let imm64_31 = Literal::constant(imm64, 31);
+    group.legalize(
+        def!(a = clz.I32(x)),
+        vec![
+            def!(c_minus_one = iconst(imm64_minus_one)),
+            def!(c_thirty_one = iconst(imm64_31)),
+            def!((index1, r2flags) = x86_bsr(x)),
+            def!(index2 = selectif(intcc_eq, r2flags, c_minus_one, index1)),
+            def!(a = isub(c_thirty_one, index2)),
+        ],
+    );
+
+    let imm64_64 = Literal::constant(imm64, 64);
+    group.legalize(
+        def!(a = ctz.I64(x)),
+        vec![
+            def!(c_sixty_four = iconst(imm64_64)),
+            def!((index1, r2flags) = x86_bsf(x)),
+            def!(a = selectif(intcc_eq, r2flags, c_sixty_four, index1)),
+        ],
+    );
+
+    let imm64_32 = Literal::constant(imm64, 32);
+    group.legalize(
+        def!(a = ctz.I32(x)),
+        vec![
+            def!(c_thirty_two = iconst(imm64_32)),
+            def!((index1, r2flags) = x86_bsf(x)),
+            def!(a = selectif(intcc_eq, r2flags, c_thirty_two, index1)),
+        ],
+    );
+
+    // Population count for baseline x86_64
+    let qv1 = var("qv1");
+    let qv3 = var("qv3");
+    let qv4 = var("qv4");
+    let qv5 = var("qv5");
+    let qv6 = var("qv6");
+    let qv7 = var("qv7");
+    let qv8 = var("qv8");
+    let qv9 = var("qv9");
+    let qv10 = var("qv10");
+    let qv11 = var("qv11");
+    let qv12 = var("qv12");
+    let qv13 = var("qv13");
+    let qv14 = var("qv14");
+    let qv15 = var("qv15");
+    let qv16 = var("qv16");
+    let qc77 = var("qc77");
+    #[allow(non_snake_case)]
+    let qc0F = var("qc0F");
+    let qc01 = var("qc01");
+
+    let imm64_1 = Literal::constant(imm64, 1);
+    let imm64_4 = Literal::constant(imm64, 4);
+    group.legalize(
+        def!(qv16 = popcnt.I64(qv1)),
+        vec![
+            def!(qv3 = ushr_imm(qv1, imm64_1)),
+            def!(qc77 = iconst(Literal::constant(imm64, 0x7777777777777777))),
+            def!(qv4 = band(qv3, qc77)),
+            def!(qv5 = isub(qv1, qv4)),
+            def!(qv6 = ushr_imm(qv4, imm64_1)),
+            def!(qv7 = band(qv6, qc77)),
+            def!(qv8 = isub(qv5, qv7)),
+            def!(qv9 = ushr_imm(qv7, imm64_1)),
+            def!(qv10 = band(qv9, qc77)),
+            def!(qv11 = isub(qv8, qv10)),
+            def!(qv12 = ushr_imm(qv11, imm64_4)),
+            def!(qv13 = iadd(qv11, qv12)),
+            def!(qc0F = iconst(Literal::constant(imm64, 0x0F0F0F0F0F0F0F0F))),
+            def!(qv14 = band(qv13, qc0F)),
+            def!(qc01 = iconst(Literal::constant(imm64, 0x0101010101010101))),
+            def!(qv15 = imul(qv14, qc01)),
+            def!(qv16 = ushr_imm(qv15, Literal::constant(imm64, 56))),
+        ],
+    );
+
+    let lv1 = var("lv1");
+    let lv3 = var("lv3");
+    let lv4 = var("lv4");
+    let lv5 = var("lv5");
+    let lv6 = var("lv6");
+    let lv7 = var("lv7");
+    let lv8 = var("lv8");
+    let lv9 = var("lv9");
+    let lv10 = var("lv10");
+    let lv11 = var("lv11");
+    let lv12 = var("lv12");
+    let lv13 = var("lv13");
+    let lv14 = var("lv14");
+    let lv15 = var("lv15");
+    let lv16 = var("lv16");
+    let lc77 = var("lc77");
+    #[allow(non_snake_case)]
+    let lc0F = var("lc0F");
+    let lc01 = var("lc01");
+
+    group.legalize(
+        def!(lv16 = popcnt.I32(lv1)),
+        vec![
+            def!(lv3 = ushr_imm(lv1, imm64_1)),
+            def!(lc77 = iconst(Literal::constant(imm64, 0x77777777))),
+            def!(lv4 = band(lv3, lc77)),
+            def!(lv5 = isub(lv1, lv4)),
+            def!(lv6 = ushr_imm(lv4, imm64_1)),
+            def!(lv7 = band(lv6, lc77)),
+            def!(lv8 = isub(lv5, lv7)),
+            def!(lv9 = ushr_imm(lv7, imm64_1)),
+            def!(lv10 = band(lv9, lc77)),
+            def!(lv11 = isub(lv8, lv10)),
+            def!(lv12 = ushr_imm(lv11, imm64_4)),
+            def!(lv13 = iadd(lv11, lv12)),
+            def!(lc0F = iconst(Literal::constant(imm64, 0x0F0F0F0F))),
+            def!(lv14 = band(lv13, lc0F)),
+            def!(lc01 = iconst(Literal::constant(imm64, 0x01010101))),
+            def!(lv15 = imul(lv14, lc01)),
+            def!(lv16 = ushr_imm(lv15, Literal::constant(imm64, 24))),
+        ],
+    );
+
+    group.finish_and_add_to(&mut shared.transform_groups);
+}
--- a/third_party/rust/cranelift-codegen-meta/src/isa/x86/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/x86/mod.rs
@@ -1,17 +1,22 @@
-mod instructions;
-
+use crate::cdsl::cpu_modes::CpuMode;
 use crate::cdsl::isa::TargetIsa;
 use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
 use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
 
+use crate::shared::types::Bool::B1;
+use crate::shared::types::Float::{F32, F64};
+use crate::shared::types::Int::{I16, I32, I64, I8};
 use crate::shared::Definitions as SharedDefinitions;
 
-fn define_settings(_shared: &SettingGroup) -> SettingGroup {
+mod instructions;
+mod legalize;
+
+fn define_settings(shared: &SettingGroup) -> SettingGroup {
     let mut settings = SettingGroupBuilder::new("x86");
 
     // CPUID.01H:ECX
     let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false);
     let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false);
     let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false);
     let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false);
     let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false);
@@ -37,16 +42,36 @@ fn define_settings(_shared: &SettingGrou
     );
 
     settings.add_predicate("use_sse41", predicate!(has_sse41));
     settings.add_predicate("use_sse42", predicate!(has_sse41 && has_sse42));
     settings.add_predicate("use_popcnt", predicate!(has_popcnt && has_sse42));
     settings.add_predicate("use_bmi1", predicate!(has_bmi1));
     settings.add_predicate("use_lznct", predicate!(has_lzcnt));
 
+    // Some shared boolean values are used in x86 instruction predicates, so we need to group them
+    // in the same TargetIsa, for compabitibity with code generated by meta-python.
+    // TODO Once all the meta generation code has been migrated from Python to Rust, we can put it
+    // back in the shared SettingGroup, and use it in x86 instruction predicates.
+
+    let is_pic = shared.get_bool("is_pic");
+    let allones_funcaddrs = shared.get_bool("allones_funcaddrs");
+    settings.add_predicate("is_pic", predicate!(is_pic));
+    settings.add_predicate("not_is_pic", predicate!(!is_pic));
+    settings.add_predicate(
+        "all_ones_funcaddrs_and_not_is_pic",
+        predicate!(allones_funcaddrs && !is_pic),
+    );
+    settings.add_predicate(
+        "not_all_ones_funcaddrs_and_not_is_pic",
+        predicate!(!allones_funcaddrs && !is_pic),
+    );
+
+    // Presets corresponding to x86 CPUs.
+
     settings.add_preset("baseline", preset!());
     let nehalem = settings.add_preset(
         "nehalem",
         preset!(has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt),
     );
     let haswell = settings.add_preset(
         "haswell",
         preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt),
@@ -113,11 +138,42 @@ fn define_registers() -> IsaRegs {
     regs.finish()
 }
 
 pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
     let settings = define_settings(&shared_defs.settings);
     let regs = define_registers();
 
     let inst_group = instructions::define(&shared_defs.format_registry);
+    legalize::define(shared_defs, &inst_group);
 
-    TargetIsa::new("x86", inst_group, settings, regs)
+    // CPU modes for 32-bit and 64-bit operations.
+    let mut x86_64 = CpuMode::new("I64");
+    let mut x86_32 = CpuMode::new("I32");
+
+    let expand_flags = shared_defs.transform_groups.by_name("expand_flags");
+    let narrow = shared_defs.transform_groups.by_name("narrow");
+    let widen = shared_defs.transform_groups.by_name("widen");
+    let x86_expand = shared_defs.transform_groups.by_name("x86_expand");
+
+    x86_32.legalize_monomorphic(expand_flags);
+    x86_32.legalize_default(narrow);
+    x86_32.legalize_type(B1, expand_flags);
+    x86_32.legalize_type(I8, widen);
+    x86_32.legalize_type(I16, widen);
+    x86_32.legalize_type(I32, x86_expand);
+    x86_32.legalize_type(F32, x86_expand);
+    x86_32.legalize_type(F64, x86_expand);
+
+    x86_64.legalize_monomorphic(expand_flags);
+    x86_64.legalize_default(narrow);
+    x86_64.legalize_type(B1, expand_flags);
+    x86_64.legalize_type(I8, widen);
+    x86_64.legalize_type(I16, widen);
+    x86_64.legalize_type(I32, x86_expand);
+    x86_64.legalize_type(I64, x86_expand);
+    x86_64.legalize_type(F32, x86_expand);
+    x86_64.legalize_type(F64, x86_expand);
+
+    let cpu_modes = vec![x86_64, x86_32];
+
+    TargetIsa::new("x86", inst_group, settings, regs, cpu_modes)
 }
--- a/third_party/rust/cranelift-codegen-meta/src/lib.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/lib.rs
@@ -1,16 +1,17 @@
 #[macro_use]
 mod cdsl;
 mod srcgen;
 
 pub mod error;
 pub mod isa;
 
 mod gen_inst;
+mod gen_legalizer;
 mod gen_registers;
 mod gen_settings;
 mod gen_types;
 
 mod constant_hash;
 mod shared;
 mod unique_table;
 
@@ -21,17 +22,17 @@ pub fn isa_from_arch(arch: &str) -> Resu
 /// Generates all the Rust source files used in Cranelift from the meta-language.
 pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error> {
     // Common definitions.
     let mut shared_defs = shared::define();
 
     gen_settings::generate(
         &shared_defs.settings,
         gen_settings::ParentGroup::None,
-        "new_settings.rs",
+        "settings.rs",
         &out_dir,
     )?;
     gen_types::generate("types.rs", &out_dir)?;
 
     // Per ISA definitions.
     let isas = isa::define(isas, &mut shared_defs);
 
     let mut all_inst_groups = vec![&shared_defs.instructions];
@@ -40,20 +41,28 @@ pub fn generate(isas: &Vec<isa::Isa>, ou
     gen_inst::generate(
         all_inst_groups,
         &shared_defs.format_registry,
         "opcodes.rs",
         "inst_builder.rs",
         &out_dir,
     )?;
 
+    gen_legalizer::generate(
+        &isas,
+        &shared_defs.format_registry,
+        &shared_defs.transform_groups,
+        "legalize",
+        &out_dir,
+    )?;
+
     for isa in isas {
         gen_registers::generate(&isa, &format!("registers-{}.rs", isa.name), &out_dir)?;
         gen_settings::generate(
             &isa.settings,
             gen_settings::ParentGroup::Shared,
-            &format!("new_settings-{}", isa.name),
+            &format!("settings-{}.rs", isa.name),
             &out_dir,
         )?;
     }
 
     Ok(())
 }
--- a/third_party/rust/cranelift-codegen-meta/src/shared/immediates.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/immediates.rs
@@ -66,20 +66,20 @@ pub fn define() -> Vec<OperandKind> {
     kinds.push(boolean);
 
     // A condition code for comparing integer values.
     // This enumerated operand kind is used for the `icmp` instruction and corresponds to the
     // condcodes::IntCC` Rust type.
     let mut intcc_values = HashMap::new();
     intcc_values.insert("eq", "Equal");
     intcc_values.insert("ne", "NotEqual");
-    intcc_values.insert("sge", "UnsignedGreaterThanOrEqual");
-    intcc_values.insert("sgt", "UnsignedGreaterThan");
-    intcc_values.insert("sle", "UnsignedLessThanOrEqual");
-    intcc_values.insert("slt", "UnsignedLessThan");
+    intcc_values.insert("sge", "SignedGreaterThanOrEqual");
+    intcc_values.insert("sgt", "SignedGreaterThan");
+    intcc_values.insert("sle", "SignedLessThanOrEqual");
+    intcc_values.insert("slt", "SignedLessThan");
     intcc_values.insert("uge", "UnsignedGreaterThanOrEqual");
     intcc_values.insert("ugt", "UnsignedGreaterThan");
     intcc_values.insert("ule", "UnsignedLessThanOrEqual");
     intcc_values.insert("ult", "UnsignedLessThan");
     let intcc = Builder::new_enum("intcc", intcc_values)
         .doc("An integer comparison condition code.")
         .default_member("cond")
         .rust_type("ir::condcodes::IntCC")
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/legalize.rs
@@ -0,0 +1,785 @@
+use crate::cdsl::ast::{bind, var, ExprBuilder, Literal};
+use crate::cdsl::inst::{Instruction, InstructionGroup};
+use crate::cdsl::xform::{TransformGroupBuilder, TransformGroups};
+
+use crate::shared::OperandKinds;
+
+use crate::shared::types::Float::{F32, F64};
+use crate::shared::types::Int::{I16, I32, I64, I8};
+
+pub fn define(insts: &InstructionGroup, immediates: &OperandKinds) -> TransformGroups {
+    let mut narrow = TransformGroupBuilder::new(
+        "narrow",
+        r#"
+        Legalize instructions by narrowing.
+
+        The transformations in the 'narrow' group work by expressing
+        instructions in terms of smaller types. Operations on vector types are
+        expressed in terms of vector types with fewer lanes, and integer
+        operations are expressed in terms of smaller integer types.
+    "#,
+    );
+
+    let mut widen = TransformGroupBuilder::new(
+        "widen",
+        r#"
+        Legalize instructions by widening.
+
+        The transformations in the 'widen' group work by expressing
+        instructions in terms of larger types.
+    "#,
+    );
+
+    let mut expand = TransformGroupBuilder::new(
+        "expand",
+        r#"
+        Legalize instructions by expansion.
+
+        Rewrite instructions in terms of other instructions, generally
+        operating on the same types as the original instructions.
+    "#,
+    );
+
+    // List of instructions.
+    let band = insts.by_name("band");
+    let band_imm = insts.by_name("band_imm");
+    let band_not = insts.by_name("band_not");
+    let bint = insts.by_name("bint");
+    let bitrev = insts.by_name("bitrev");
+    let bnot = insts.by_name("bnot");
+    let bor = insts.by_name("bor");
+    let bor_imm = insts.by_name("bor_imm");
+    let bor_not = insts.by_name("bor_not");
+    let br_icmp = insts.by_name("br_icmp");
+    let br_table = insts.by_name("br_table");
+    let bxor = insts.by_name("bxor");
+    let bxor_imm = insts.by_name("bxor_imm");
+    let bxor_not = insts.by_name("bxor_not");
+    let cls = insts.by_name("cls");
+    let clz = insts.by_name("clz");
+    let ctz = insts.by_name("ctz");
+    let fabs = insts.by_name("fabs");
+    let f32const = insts.by_name("f32const");
+    let f64const = insts.by_name("f64const");
+    let fcopysign = insts.by_name("fcopysign");
+    let fneg = insts.by_name("fneg");
+    let iadd = insts.by_name("iadd");
+    let iadd_carry = insts.by_name("iadd_carry");
+    let iadd_cin = insts.by_name("iadd_cin");
+    let iadd_cout = insts.by_name("iadd_cout");
+    let iadd_imm = insts.by_name("iadd_imm");
+    let icmp = insts.by_name("icmp");
+    let icmp_imm = insts.by_name("icmp_imm");
+    let iconcat = insts.by_name("iconcat");
+    let iconst = insts.by_name("iconst");
+    let ifcmp = insts.by_name("ifcmp");
+    let ifcmp_imm = insts.by_name("ifcmp_imm");
+    let imul = insts.by_name("imul");
+    let imul_imm = insts.by_name("imul_imm");
+    let ireduce = insts.by_name("ireduce");
+    let irsub_imm = insts.by_name("irsub_imm");
+    let ishl = insts.by_name("ishl");
+    let ishl_imm = insts.by_name("ishl_imm");
+    let isplit = insts.by_name("isplit");
+    let istore8 = insts.by_name("istore8");
+    let istore16 = insts.by_name("istore16");
+    let isub = insts.by_name("isub");
+    let isub_bin = insts.by_name("isub_bin");
+    let isub_borrow = insts.by_name("isub_borrow");
+    let isub_bout = insts.by_name("isub_bout");
+    let load = insts.by_name("load");
+    let popcnt = insts.by_name("popcnt");
+    let rotl = insts.by_name("rotl");
+    let rotl_imm = insts.by_name("rotl_imm");
+    let rotr = insts.by_name("rotr");
+    let rotr_imm = insts.by_name("rotr_imm");
+    let sdiv = insts.by_name("sdiv");
+    let sdiv_imm = insts.by_name("sdiv_imm");
+    let select = insts.by_name("select");
+    let sextend = insts.by_name("sextend");
+    let sshr = insts.by_name("sshr");
+    let sshr_imm = insts.by_name("sshr_imm");
+    let srem = insts.by_name("srem");
+    let srem_imm = insts.by_name("srem_imm");
+    let store = insts.by_name("store");
+    let udiv = insts.by_name("udiv");
+    let udiv_imm = insts.by_name("udiv_imm");
+    let uextend = insts.by_name("uextend");
+    let uload8 = insts.by_name("uload8");
+    let uload16 = insts.by_name("uload16");
+    let ushr = insts.by_name("ushr");
+    let ushr_imm = insts.by_name("ushr_imm");
+    let urem = insts.by_name("urem");
+    let urem_imm = insts.by_name("urem_imm");
+    let trapif = insts.by_name("trapif");
+    let trapnz = insts.by_name("trapnz");
+    let trapz = insts.by_name("trapz");
+
+    // Custom expansions for memory objects.
+    expand.custom_legalize(insts.by_name("global_value"), "expand_global_value");
+    expand.custom_legalize(insts.by_name("heap_addr"), "expand_heap_addr");
+    expand.custom_legalize(insts.by_name("table_addr"), "expand_table_addr");
+
+    // Custom expansions for calls.
+    expand.custom_legalize(insts.by_name("call"), "expand_call");
+
+    // Custom expansions that need to change the CFG.
+    // TODO: Add sufficient XForm syntax that we don't need to hand-code these.
+    expand.custom_legalize(trapz, "expand_cond_trap");
+    expand.custom_legalize(trapnz, "expand_cond_trap");
+    expand.custom_legalize(br_table, "expand_br_table");
+    expand.custom_legalize(select, "expand_select");
+
+    // Custom expansions for floating point constants.
+    // These expansions require bit-casting or creating constant pool entries.
+    expand.custom_legalize(f32const, "expand_fconst");
+    expand.custom_legalize(f64const, "expand_fconst");
+
+    // Custom expansions for stack memory accesses.
+    expand.custom_legalize(insts.by_name("stack_load"), "expand_stack_load");
+    expand.custom_legalize(insts.by_name("stack_store"), "expand_stack_store");
+
+    // List of immediates.
+    let imm64 = immediates.by_name("imm64");
+    let ieee32 = immediates.by_name("ieee32");
+    let ieee64 = immediates.by_name("ieee64");
+    let intcc = immediates.by_name("intcc");
+
+    // List of variables to reuse in patterns.
+    let x = var("x");
+    let y = var("y");
+    let z = var("z");
+    let a = var("a");
+    let a1 = var("a1");
+    let a2 = var("a2");
+    let a3 = var("a3");
+    let a4 = var("a4");
+    let b = var("b");
+    let b1 = var("b1");
+    let b2 = var("b2");
+    let b3 = var("b3");
+    let b4 = var("b4");
+    let b_in = var("b_in");
+    let b_int = var("b_int");
+    let c = var("c");
+    let c1 = var("c1");
+    let c2 = var("c2");
+    let c3 = var("c3");
+    let c4 = var("c4");
+    let c_in = var("c_in");
+    let c_int = var("c_int");
+    let d = var("d");
+    let d1 = var("d1");
+    let d2 = var("d2");
+    let d3 = var("d3");
+    let d4 = var("d4");
+    let e = var("e");
+    let e1 = var("e1");
+    let e2 = var("e2");
+    let e3 = var("e3");
+    let e4 = var("e4");
+    let f = var("f");
+    let f1 = var("f1");
+    let f2 = var("f2");
+    let xl = var("xl");
+    let xh = var("xh");
+    let yl = var("yl");
+    let yh = var("yh");
+    let al = var("al");
+    let ah = var("ah");
+    let cc = var("cc");
+    let ptr = var("ptr");
+    let flags = var("flags");
+    let offset = var("off");
+
+    narrow.legalize(
+        def!(a = iadd(x, y)),
+        vec![
+            def!((xl, xh) = isplit(x)),
+            def!((yl, yh) = isplit(y)),
+            def!((al, c) = iadd_cout(xl, yl)),
+            def!(ah = iadd_cin(xh, yh, c)),
+            def!(a = iconcat(al, ah)),
+        ],
+    );
+
+    narrow.legalize(
+        def!(a = isub(x, y)),
+        vec![
+            def!((xl, xh) = isplit(x)),
+            def!((yl, yh) = isplit(y)),
+            def!((al, b) = isub_bout(xl, yl)),
+            def!(ah = isub_bin(xh, yh, b)),
+            def!(a = iconcat(al, ah)),
+        ],
+    );
+
+    for &bin_op in &[band, bor, bxor] {
+        narrow.legalize(
+            def!(a = bin_op(x, y)),
+            vec![
+                def!((xl, xh) = isplit(x)),
+                def!((yl, yh) = isplit(y)),
+                def!(al = bin_op(xl, yl)),
+                def!(ah = bin_op(xh, yh)),
+                def!(a = iconcat(al, ah)),
+            ],
+        );
+    }
+
+    narrow.legalize(
+        def!(a = select(c, x, y)),
+        vec![
+            def!((xl, xh) = isplit(x)),
+            def!((yl, yh) = isplit(y)),
+            def!(al = select(c, xl, yl)),
+            def!(ah = select(c, xh, yh)),
+            def!(a = iconcat(al, ah)),
+        ],
+    );
+
+    // Widen instructions with one input operand.
+    for &op in &[bnot, popcnt] {
+        for &int_ty in &[I8, I16] {
+            widen.legalize(
+                def!(a = op.int_ty(b)),
+                vec![
+                    def!(x = uextend.I32(b)),
+                    def!(z = op.I32(x)),
+                    def!(a = ireduce.int_ty(z)),
+                ],
+            );
+        }
+    }
+
+    // Widen instructions with two input operands.
+    let mut widen_two_arg = |signed: bool, op: &Instruction| {
+        for &int_ty in &[I8, I16] {
+            let sign_ext_op = if signed { sextend } else { uextend };
+            widen.legalize(
+                def!(a = op.int_ty(b, c)),
+                vec![
+                    def!(x = sign_ext_op.I32(b)),
+                    def!(y = sign_ext_op.I32(c)),
+                    def!(z = op.I32(x, y)),
+                    def!(a = ireduce.int_ty(z)),
+                ],
+            );
+        }
+    };
+
+    for bin_op in &[
+        iadd, isub, imul, udiv, urem, band, bor, bxor, band_not, bor_not, bxor_not,
+    ] {
+        widen_two_arg(false, bin_op);
+    }
+    for bin_op in &[sdiv, srem] {
+        widen_two_arg(true, bin_op);
+    }
+
+    // Widen instructions using immediate operands.
+    let mut widen_imm = |signed: bool, op: &Instruction| {
+        for &int_ty in &[I8, I16] {
+            let sign_ext_op = if signed { sextend } else { uextend };
+            widen.legalize(
+                def!(a = op.int_ty(b, c)),
+                vec![
+                    def!(x = sign_ext_op.I32(b)),
+                    def!(z = op.I32(x, c)),
+                    def!(a = ireduce.int_ty(z)),
+                ],
+            );
+        }
+    };
+
+    for bin_op in &[
+        iadd_imm, imul_imm, udiv_imm, urem_imm, band_imm, bor_imm, bxor_imm, irsub_imm,
+    ] {
+        widen_imm(false, bin_op);
+    }
+    for bin_op in &[sdiv_imm, srem_imm] {
+        widen_imm(true, bin_op);
+    }
+
+    for &(int_ty, num) in &[(I8, 24), (I16, 16)] {
+        let imm = Literal::constant(imm64, -num);
+
+        widen.legalize(
+            def!(a = clz.int_ty(b)),
+            vec![
+                def!(c = uextend.I32(b)),
+                def!(d = clz.I32(c)),
+                def!(e = iadd_imm(d, imm)),
+                def!(a = ireduce.int_ty(e)),
+            ],
+        );
+
+        widen.legalize(
+            def!(a = cls.int_ty(b)),
+            vec![
+                def!(c = sextend.I32(b)),
+                def!(d = cls.I32(c)),
+                def!(e = iadd_imm(d, imm)),
+                def!(a = ireduce.int_ty(e)),
+            ],
+        );
+    }
+
+    for &(int_ty, num) in &[(I8, 1 << 8), (I16, 1 << 16)] {
+        let num = Literal::constant(imm64, num);
+        widen.legalize(
+            def!(a = ctz.int_ty(b)),
+            vec![
+                def!(c = uextend.I32(b)),
+                // When `b` is zero, returns the size of x in bits.
+                def!(d = bor_imm(c, num)),
+                def!(e = ctz.I32(d)),
+                def!(a = ireduce.int_ty(e)),
+            ],
+        );
+    }
+
+    // iconst
+    for &int_ty in &[I8, I16] {
+        widen.legalize(
+            def!(a = iconst.int_ty(b)),
+            vec![def!(c = iconst.I32(b)), def!(a = ireduce.int_ty(c))],
+        );
+    }
+
+    for &extend_op in &[uextend, sextend] {
+        // The sign extension operators have two typevars: the result has one and controls the
+        // instruction, then the input has one.
+        let bound = bind(bind(extend_op, I16), I8);
+        widen.legalize(
+            def!(a = bound(b)),
+            vec![def!(c = extend_op.I32(b)), def!(a = ireduce(c))],
+        );
+    }
+
+    widen.legalize(
+        def!(store.I8(flags, a, ptr, offset)),
+        vec![
+            def!(b = uextend.I32(a)),
+            def!(istore8(flags, b, ptr, offset)),
+        ],
+    );
+
+    widen.legalize(
+        def!(store.I16(flags, a, ptr, offset)),
+        vec![
+            def!(b = uextend.I32(a)),
+            def!(istore16(flags, b, ptr, offset)),
+        ],
+    );
+
+    widen.legalize(
+        def!(a = load.I8(flags, ptr, offset)),
+        vec![
+            def!(b = uload8.I32(flags, ptr, offset)),
+            def!(a = ireduce(b)),
+        ],
+    );
+
+    widen.legalize(
+        def!(a = load.I16(flags, ptr, offset)),
+        vec![
+            def!(b = uload16.I32(flags, ptr, offset)),
+            def!(a = ireduce(b)),
+        ],
+    );
+
+    for &int_ty in &[I8, I16] {
+        widen.legalize(
+            def!(br_table.int_ty(x, y, z)),
+            vec![def!(b = uextend.I32(x)), def!(br_table(b, y, z))],
+        );
+    }
+
+    for &int_ty in &[I8, I16] {
+        widen.legalize(
+            def!(a = bint.int_ty(b)),
+            vec![def!(x = bint.I32(b)), def!(a = ireduce.int_ty(x))],
+        );
+    }
+
+    for &int_ty in &[I8, I16] {
+        for &op in &[ishl, ishl_imm, ushr, ushr_imm] {
+            widen.legalize(
+                def!(a = op.int_ty(b, c)),
+                vec![
+                    def!(x = uextend.I32(b)),
+                    def!(z = op.I32(x, c)),
+                    def!(a = ireduce.int_ty(z)),
+                ],
+            );
+        }
+
+        for &op in &[sshr, sshr_imm] {
+            widen.legalize(
+                def!(a = op.int_ty(b, c)),
+                vec![
+                    def!(x = sextend.I32(b)),
+                    def!(z = op.I32(x, c)),
+                    def!(a = ireduce.int_ty(z)),
+                ],
+            );
+        }
+
+        for cc in &["eq", "ne", "ugt", "ult", "uge", "ule"] {
+            let w_cc = Literal::enumerator_for(intcc, cc);
+            widen.legalize(
+                def!(a = icmp_imm.int_ty(w_cc, b, c)),
+                vec![def!(x = uextend.I32(b)), def!(a = icmp_imm(w_cc, x, c))],
+            );
+            widen.legalize(
+                def!(a = icmp.int_ty(w_cc, b, c)),
+                vec![
+                    def!(x = uextend.I32(b)),
+                    def!(y = uextend.I32(c)),
+                    def!(a = icmp.I32(w_cc, x, y)),
+                ],
+            );
+        }
+
+        for cc in &["sgt", "slt", "sge", "sle"] {
+            let w_cc = Literal::enumerator_for(intcc, cc);
+            widen.legalize(
+                def!(a = icmp_imm.int_ty(w_cc, b, c)),
+                vec![def!(x = sextend.I32(b)), def!(a = icmp_imm(w_cc, x, c))],
+            );
+
+            widen.legalize(
+                def!(a = icmp.int_ty(w_cc, b, c)),
+                vec![
+                    def!(x = sextend.I32(b)),
+                    def!(y = sextend.I32(c)),
+                    def!(a = icmp(w_cc, x, y)),
+                ],
+            );
+        }
+    }
+
+    // Expand integer operations with carry for RISC architectures that don't have
+    // the flags.
+    let intcc_ult = Literal::enumerator_for(intcc, "ult");
+    expand.legalize(
+        def!((a, c) = iadd_cout(x, y)),
+        vec![def!(a = iadd(x, y)), def!(c = icmp(intcc_ult, a, x))],
+    );
+
+    let intcc_ugt = Literal::enumerator_for(intcc, "ugt");
+    expand.legalize(
+        def!((a, b) = isub_bout(x, y)),
+        vec![def!(a = isub(x, y)), def!(b = icmp(intcc_ugt, a, x))],
+    );
+
+    expand.legalize(
+        def!(a = iadd_cin(x, y, c)),
+        vec![
+            def!(a1 = iadd(x, y)),
+            def!(c_int = bint(c)),
+            def!(a = iadd(a1, c_int)),
+        ],
+    );
+
+    expand.legalize(
+        def!(a = isub_bin(x, y, b)),
+        vec![
+            def!(a1 = isub(x, y)),
+            def!(b_int = bint(b)),
+            def!(a = isub(a1, b_int)),
+        ],
+    );
+
+    expand.legalize(
+        def!((a, c) = iadd_carry(x, y, c_in)),
+        vec![
+            def!((a1, c1) = iadd_cout(x, y)),
+            def!(c_int = bint(c_in)),
+            def!((a, c2) = iadd_cout(a1, c_int)),
+            def!(c = bor(c1, c2)),
+        ],
+    );
+
+    expand.legalize(
+        def!((a, b) = isub_borrow(x, y, b_in)),
+        vec![
+            def!((a1, b1) = isub_bout(x, y)),
+            def!(b_int = bint(b_in)),
+            def!((a, b2) = isub_bout(a1, b_int)),
+            def!(b = bor(b1, b2)),
+        ],
+    );
+
+    // Expansions for immediate operands that are out of range.
+    for &(inst_imm, inst) in &[
+        (iadd_imm, iadd),
+        (imul_imm, imul),
+        (sdiv_imm, sdiv),
+        (udiv_imm, udiv),
+        (srem_imm, srem),
+        (urem_imm, urem),
+        (band_imm, band),
+        (bor_imm, bor),
+        (bxor_imm, bxor),
+        (ifcmp_imm, ifcmp),
+    ] {
+        expand.legalize(
+            def!(a = inst_imm(x, y)),
+            vec![def!(a1 = iconst(y)), def!(a = inst(x, a1))],
+        );
+    }
+
+    expand.legalize(
+        def!(a = irsub_imm(y, x)),
+        vec![def!(a1 = iconst(x)), def!(a = isub(a1, y))],
+    );
+
+    // Rotates and shifts.
+    for &(inst_imm, inst) in &[
+        (rotl_imm, rotl),
+        (rotr_imm, rotr),
+        (ishl_imm, ishl),
+        (sshr_imm, sshr),
+        (ushr_imm, ushr),
+    ] {
+        expand.legalize(
+            def!(a = inst_imm(x, y)),
+            vec![def!(a1 = iconst.I32(y)), def!(a = inst(x, a1))],
+        );
+    }
+
+    expand.legalize(
+        def!(a = icmp_imm(cc, x, y)),
+        vec![def!(a1 = iconst(y)), def!(a = icmp(cc, x, a1))],
+    );
+
+    //# Expansions for *_not variants of bitwise ops.
+    for &(inst_not, inst) in &[(band_not, band), (bor_not, bor), (bxor_not, bxor)] {
+        expand.legalize(
+            def!(a = inst_not(x, y)),
+            vec![def!(a1 = bnot(y)), def!(a = inst(x, a1))],
+        );
+    }
+
+    //# Expand bnot using xor.
+    let minus_one = Literal::constant(imm64, -1);
+    expand.legalize(
+        def!(a = bnot(x)),
+        vec![def!(y = iconst(minus_one)), def!(a = bxor(x, y))],
+    );
+
+    //# Expand bitrev
+    //# Adapted from Stack Overflow.
+    //# https://stackoverflow.com/questions/746171/most-efficient-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c
+    let imm64_1 = Literal::constant(imm64, 1);
+    let imm64_2 = Literal::constant(imm64, 2);
+    let imm64_4 = Literal::constant(imm64, 4);
+
+    widen.legalize(
+        def!(a = bitrev.I8(x)),
+        vec![
+            def!(a1 = band_imm(x, Literal::constant(imm64, 0xaa))),
+            def!(a2 = ushr_imm(a1, imm64_1)),
+            def!(a3 = band_imm(x, Literal::constant(imm64, 0x55))),
+            def!(a4 = ishl_imm(a3, imm64_1)),
+            def!(b = bor(a2, a4)),
+            def!(b1 = band_imm(b, Literal::constant(imm64, 0xcc))),
+            def!(b2 = ushr_imm(b1, imm64_2)),
+            def!(b3 = band_imm(b, Literal::constant(imm64, 0x33))),
+            def!(b4 = ishl_imm(b3, imm64_2)),
+            def!(c = bor(b2, b4)),
+            def!(c1 = band_imm(c, Literal::constant(imm64, 0xf0))),
+            def!(c2 = ushr_imm(c1, imm64_4)),
+            def!(c3 = band_imm(c, Literal::constant(imm64, 0x0f))),
+            def!(c4 = ishl_imm(c3, imm64_4)),
+            def!(a = bor(c2, c4)),
+        ],
+    );
+
+    let imm64_8 = Literal::constant(imm64, 8);
+
+    widen.legalize(
+        def!(a = bitrev.I16(x)),
+        vec![
+            def!(a1 = band_imm(x, Literal::constant(imm64, 0xaaaa))),
+            def!(a2 = ushr_imm(a1, imm64_1)),
+            def!(a3 = band_imm(x, Literal::constant(imm64, 0x5555))),
+            def!(a4 = ishl_imm(a3, imm64_1)),
+            def!(b = bor(a2, a4)),
+            def!(b1 = band_imm(b, Literal::constant(imm64, 0xcccc))),
+            def!(b2 = ushr_imm(b1, imm64_2)),
+            def!(b3 = band_imm(b, Literal::constant(imm64, 0x3333))),
+            def!(b4 = ishl_imm(b3, imm64_2)),
+            def!(c = bor(b2, b4)),
+            def!(c1 = band_imm(c, Literal::constant(imm64, 0xf0f0))),
+            def!(c2 = ushr_imm(c1, imm64_4)),
+            def!(c3 = band_imm(c, Literal::constant(imm64, 0x0f0f))),
+            def!(c4 = ishl_imm(c3, imm64_4)),
+            def!(d = bor(c2, c4)),
+            def!(d1 = band_imm(d, Literal::constant(imm64, 0xff00))),
+            def!(d2 = ushr_imm(d1, imm64_8)),
+            def!(d3 = band_imm(d, Literal::constant(imm64, 0x00ff))),
+            def!(d4 = ishl_imm(d3, imm64_8)),
+            def!(a = bor(d2, d4)),
+        ],
+    );
+
+    let imm64_16 = Literal::constant(imm64, 16);
+
+    expand.legalize(
+        def!(a = bitrev.I32(x)),
+        vec![
+            def!(a1 = band_imm(x, Literal::constant(imm64, 0xaaaaaaaa))),
+            def!(a2 = ushr_imm(a1, imm64_1)),
+            def!(a3 = band_imm(x, Literal::constant(imm64, 0x55555555))),
+            def!(a4 = ishl_imm(a3, imm64_1)),
+            def!(b = bor(a2, a4)),
+            def!(b1 = band_imm(b, Literal::constant(imm64, 0xcccccccc))),
+            def!(b2 = ushr_imm(b1, imm64_2)),
+            def!(b3 = band_imm(b, Literal::constant(imm64, 0x33333333))),
+            def!(b4 = ishl_imm(b3, imm64_2)),
+            def!(c = bor(b2, b4)),
+            def!(c1 = band_imm(c, Literal::constant(imm64, 0xf0f0f0f0))),
+            def!(c2 = ushr_imm(c1, imm64_4)),
+            def!(c3 = band_imm(c, Literal::constant(imm64, 0x0f0f0f0f))),
+            def!(c4 = ishl_imm(c3, imm64_4)),
+            def!(d = bor(c2, c4)),
+            def!(d1 = band_imm(d, Literal::constant(imm64, 0xff00ff00))),
+            def!(d2 = ushr_imm(d1, imm64_8)),
+            def!(d3 = band_imm(d, Literal::constant(imm64, 0x00ff00ff))),
+            def!(d4 = ishl_imm(d3, imm64_8)),
+            def!(e = bor(d2, d4)),
+            def!(e1 = ushr_imm(e, imm64_16)),
+            def!(e2 = ishl_imm(e, imm64_16)),
+            def!(a = bor(e1, e2)),
+        ],
+    );
+
+    #[allow(overflowing_literals)]
+    let imm64_0xaaaaaaaaaaaaaaaa = Literal::constant(imm64, 0xaaaaaaaaaaaaaaaa);
+    let imm64_0x5555555555555555 = Literal::constant(imm64, 0x5555555555555555);
+    #[allow(overflowing_literals)]
+    let imm64_0xcccccccccccccccc = Literal::constant(imm64, 0xcccccccccccccccc);
+    let imm64_0x3333333333333333 = Literal::constant(imm64, 0x3333333333333333);
+    #[allow(overflowing_literals)]
+    let imm64_0xf0f0f0f0f0f0f0f0 = Literal::constant(imm64, 0xf0f0f0f0f0f0f0f0);
+    let imm64_0x0f0f0f0f0f0f0f0f = Literal::constant(imm64, 0x0f0f0f0f0f0f0f0f);
+    #[allow(overflowing_literals)]
+    let imm64_0xff00ff00ff00ff00 = Literal::constant(imm64, 0xff00ff00ff00ff00);
+    let imm64_0x00ff00ff00ff00ff = Literal::constant(imm64, 0x00ff00ff00ff00ff);
+    #[allow(overflowing_literals)]
+    let imm64_0xffff0000ffff0000 = Literal::constant(imm64, 0xffff0000ffff0000);
+    let imm64_0x0000ffff0000ffff = Literal::constant(imm64, 0x0000ffff0000ffff);
+    let imm64_32 = Literal::constant(imm64, 32);
+
+    expand.legalize(
+        def!(a = bitrev.I64(x)),
+        vec![
+            def!(a1 = band_imm(x, imm64_0xaaaaaaaaaaaaaaaa)),
+            def!(a2 = ushr_imm(a1, imm64_1)),
+            def!(a3 = band_imm(x, imm64_0x5555555555555555)),
+            def!(a4 = ishl_imm(a3, imm64_1)),
+            def!(b = bor(a2, a4)),
+            def!(b1 = band_imm(b, imm64_0xcccccccccccccccc)),
+            def!(b2 = ushr_imm(b1, imm64_2)),
+            def!(b3 = band_imm(b, imm64_0x3333333333333333)),
+            def!(b4 = ishl_imm(b3, imm64_2)),
+            def!(c = bor(b2, b4)),
+            def!(c1 = band_imm(c, imm64_0xf0f0f0f0f0f0f0f0)),
+            def!(c2 = ushr_imm(c1, imm64_4)),
+            def!(c3 = band_imm(c, imm64_0x0f0f0f0f0f0f0f0f)),
+            def!(c4 = ishl_imm(c3, imm64_4)),
+            def!(d = bor(c2, c4)),
+            def!(d1 = band_imm(d, imm64_0xff00ff00ff00ff00)),
+            def!(d2 = ushr_imm(d1, imm64_8)),
+            def!(d3 = band_imm(d, imm64_0x00ff00ff00ff00ff)),
+            def!(d4 = ishl_imm(d3, imm64_8)),
+            def!(e = bor(d2, d4)),
+            def!(e1 = band_imm(e, imm64_0xffff0000ffff0000)),
+            def!(e2 = ushr_imm(e1, imm64_16)),
+            def!(e3 = band_imm(e, imm64_0x0000ffff0000ffff)),
+            def!(e4 = ishl_imm(e3, imm64_16)),
+            def!(f = bor(e2, e4)),
+            def!(f1 = ushr_imm(f, imm64_32)),
+            def!(f2 = ishl_imm(f, imm64_32)),
+            def!(a = bor(f1, f2)),
+        ],
+    );
+
+    // Floating-point sign manipulations.
+    for &(ty, const_inst, minus_zero) in &[
+        (F32, f32const, &Literal::bits(ieee32, 0x80000000)),
+        (F64, f64const, &Literal::bits(ieee64, 0x8000000000000000)),
+    ] {
+        expand.legalize(
+            def!(a = fabs.ty(x)),
+            vec![def!(b = const_inst(minus_zero)), def!(a = band_not(x, b))],
+        );
+
+        expand.legalize(
+            def!(a = fneg.ty(x)),
+            vec![def!(b = const_inst(minus_zero)), def!(a = bxor(x, b))],
+        );
+
+        expand.legalize(
+            def!(a = fcopysign.ty(x, y)),
+            vec![
+                def!(b = const_inst(minus_zero)),
+                def!(a1 = band_not(x, b)),
+                def!(a2 = band(y, b)),
+                def!(a = bor(a1, a2)),
+            ],
+        );
+    }
+
+    expand.custom_legalize(br_icmp, "expand_br_icmp");
+
+    let mut groups = TransformGroups::new();
+
+    narrow.finish_and_add_to(&mut groups);
+    let expand_id = expand.finish_and_add_to(&mut groups);
+
+    // Expansions using CPU flags.
+    let mut expand_flags = TransformGroupBuilder::new(
+        "expand_flags",
+        r#"
+        Instruction expansions for architectures with flags.
+
+        Expand some instructions using CPU flags, then fall back to the normal
+        expansions. Not all architectures support CPU flags, so these patterns
+        are kept separate.
+    "#,
+    )
+    .chain_with(expand_id);
+
+    let imm64_0 = Literal::constant(imm64, 0);
+    let intcc_ne = Literal::enumerator_for(intcc, "ne");
+    let intcc_eq = Literal::enumerator_for(intcc, "eq");
+
+    expand_flags.legalize(
+        def!(trapnz(x, c)),
+        vec![
+            def!(a = ifcmp_imm(x, imm64_0)),
+            def!(trapif(intcc_ne, a, c)),
+        ],
+    );
+
+    expand_flags.legalize(
+        def!(trapz(x, c)),
+        vec![
+            def!(a = ifcmp_imm(x, imm64_0)),
+            def!(trapif(intcc_eq, a, c)),
+        ],
+    );
+
+    expand_flags.finish_and_add_to(&mut groups);
+
+    // XXX The order of declarations unfortunately matters to be compatible with the Python code.
+    // When it's all migrated, we can put this next to the narrow/expand finish_and_add_to calls
+    // above.
+    widen.finish_and_add_to(&mut groups);
+
+    groups
+}
--- a/third_party/rust/cranelift-codegen-meta/src/shared/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/mod.rs
@@ -1,27 +1,30 @@
 //! Shared definitions for the Cranelift intermediate language.
 
 pub mod entities;
 pub mod formats;
 pub mod immediates;
 pub mod instructions;
+pub mod legalize;
 pub mod settings;
 pub mod types;
 
 use crate::cdsl::formats::FormatRegistry;
 use crate::cdsl::inst::InstructionGroup;
 use crate::cdsl::operands::OperandKind;
 use crate::cdsl::settings::SettingGroup;
+use crate::cdsl::xform::TransformGroups;
 
 pub struct Definitions {
     pub settings: SettingGroup,
     pub instructions: InstructionGroup,
     pub operand_kinds: OperandKinds,
     pub format_registry: FormatRegistry,
+    pub transform_groups: TransformGroups,
 }
 
 pub struct OperandKinds(Vec<OperandKind>);
 
 impl OperandKinds {
     pub fn new() -> Self {
         Self(Vec::new())
     }
@@ -45,15 +48,19 @@ impl OperandKinds {
         self.0.push(operand_kind);
     }
 }
 
 pub fn define() -> Definitions {
     let immediates = OperandKinds(immediates::define());
     let entities = OperandKinds(entities::define());
     let format_registry = formats::define(&immediates, &entities);
+    let instructions = instructions::define(&format_registry, &immediates, &entities);
+    let transform_groups = legalize::define(&instructions, &immediates);
+
     Definitions {
         settings: settings::define(),
-        instructions: instructions::define(&format_registry, &immediates, &entities),
+        instructions,
         operand_kinds: immediates,
         format_registry,
+        transform_groups,
     }
 }
--- a/third_party/rust/cranelift-codegen-meta/src/srcgen.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/srcgen.rs
@@ -74,37 +74,37 @@ impl Formatter {
             String::new()
         } else {
             format!("{:-1$}", " ", self.indent * SHIFTWIDTH)
         }
     }
 
     /// Get a string containing whitespace outdented one level. Used for
     /// lines of code that are inside a single indented block.
-    fn _get_outdent(&mut self) -> String {
+    fn get_outdent(&mut self) -> String {
+        self.indent_pop();
+        let s = self.get_indent();
         self.indent_push();
-        let s = self.get_indent();
-        self.indent_pop();
         s
     }
 
     /// Add an indented line.
     pub fn line(&mut self, contents: impl AsRef<str>) {
         let indented_line = format!("{}{}\n", self.get_indent(), contents.as_ref());
         self.lines.push(indented_line);
     }
 
     /// Pushes an empty line.
     pub fn empty_line(&mut self) {
         self.lines.push("\n".to_string());
     }
 
     /// Emit a line outdented one level.
-    pub fn _outdented_line(&mut self, s: &str) {
-        let new_line = format!("{}{}", self._get_outdent(), s);
+    pub fn outdented_line(&mut self, s: &str) {
+        let new_line = format!("{}{}\n", self.get_outdent(), s);
         self.lines.push(new_line);
     }
 
     /// Write `self.lines` to a file.
     pub fn update_file(
         &self,
         filename: impl AsRef<str>,
         directory: &str,
--- a/third_party/rust/cranelift-codegen-meta/src/unique_table.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/unique_table.rs
@@ -26,16 +26,19 @@ impl<'entries, T: Eq + Hash> UniqueTable
             }
             Some(&i) => i,
         }
     }
 
     pub fn len(&self) -> usize {
         self.table.len()
     }
+    pub fn get(&self, index: usize) -> &T {
+        self.table[index]
+    }
     pub fn iter(&self) -> slice::Iter<&'entries T> {
         self.table.iter()
     }
 }
 
 /// A table of sequences which tries to avoid common subsequences.
 pub struct UniqueSeqTable<T: PartialEq + Clone> {
     table: Vec<T>,
--- a/third_party/rust/cranelift-codegen/.cargo-checksum.json
+++ b/third_party/rust/cranelift-codegen/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"9e1e9d197a2fcdb9bfd3c32a7608ab772ed4301aa0b9f2e60b73c98f542ac760","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"7c0919d12ff966e0c400e7cfa0a555b2e50ede20c0dfb4f4401e35a828134ea9","meta-python/base/__init__.py":"4fac8bb055541dc964383bb04223bae6dfbfe348abf5e23f83655966cbb4aa8f","meta-python/base/entities.py":"f956b8ba863cd26f6867e13c1b2db3c202e9b07c48c1e43a9c2b41ad0deb8922","meta-python/base/formats.py":"f9fb41210bc1f99a78cb7a60ee9f01c603412c6b1db7e69abbcc0f573cf9fb40","meta-python/base/immediates.py":"f42682d86bda7b569ec2fc2debd9036355999e61caaa9fbf8307e0be8a164814","meta-python/base/instructions.py":"4f92066dc9cfa651e395afc8956b1a082b4e8591c6f7310db89593ff55322e2b","meta-python/base/legalize.py":"8455a5c7428e98d23e432d7be28cc863804781013ac614ba57f6d231cd810d28","meta-python/base/predicates.py":"53a5a9a37021a9762f0faec9f9e3c5b713411d47dd9192423cfe0f7a61916809","meta-python/base/semantics.py":"b90cbca9a143676ac37496c83634a66a83360248e4505ff7bda12b0d454efd92","meta-python/base/settings.py":"97fca9ddaab347f50f1594c93283f8f474e53a7232eae65949f56a6a702c2bba","meta-python/base/types.py":"9616d6fe4cab050827ab02eeb9843eacebbb8f7555521f504b5ee2ddf7214fdb","meta-python/build.py":"a6617c8a4d20b76e0c8ea8c6cb2e95ada9ffd0c6cf0f93d841a5204e4678f218","meta-python/cdsl/__init__.py":"b534ec129a0e517f0d13313c2b4bb3a345d3e5b62693a31d8d42c80a219e39fa","meta-python/cdsl/ast.py":"832ac98dddea88f4f8a608ac6ac3fda009a15b52abacca30cf39e3787154521c","meta-python/cdsl/formats.py":"fedfeaec754b40d6a9cc92b827976782c615d8eab1c7f47f6b47510cbef585f4","meta-python/cdsl/instructions.py":"50fe9329a56e6294a27306aad7be22c88a5b5ef42c3b030ec59218167157f62f","meta-python/cdsl/isa.py":"dc530a4dd5642e3379398dfc8a90ad6ae1692302de63851370bdfa8abf4cdce0","meta-python/cdsl/operands.py":"e24914eae4059b88781bf5a5d7a14ecf98b10a701ed6cf6e88d15981b2ccbfdf","meta-python/cdsl/predicates.py":"fe2dc6e78ff6d4bcb6b1d5448ded343ede0f03b96a965a11aef0f5bbe7bb8f56","meta-python/cdsl/registers.py":"939dafd1b8976a6cd456c9a5e4b374af81fafb9da979ea3a1f6d14e4645914a6","meta-python/cdsl/settings.py":"a859bd006e13b245ff906977512cd4822c3b22601ed17e87ce0618503172809f","meta-python/cdsl/test_ast.py":"947e934e2862445a158bf266dff58a8c88aae31fb34a7f823309ee58a15c5393","meta-python/cdsl/test_package.py":"ffa53d20e023ecb89137294bb13614f4de9b09e1bf05d9772131570bf78f7987","meta-python/cdsl/test_ti.py":"04656e2da99ec37875f91172bb2fa423da1ee27bf523a4d41af311ea22492d1b","meta-python/cdsl/test_typevar.py":"768bf3c35481950264ad6fd7059a532ac62b121ac439983bb4fe1ae76a1d1248","meta-python/cdsl/test_xform.py":"ddb6633c7941bbf68570701cb887a81d6b4b27f4bc45eabccf2ce287ad9b77e9","meta-python/cdsl/ti.py":"566e91f0bb3ebacd5abf9f5a663caee69706c2e829dee1ed735d229198689f76","meta-python/cdsl/types.py":"adee4bbc1a9478288fa4b53ee1edeb5ee3296dba2c70bfbe01e923895064999e","meta-python/cdsl/typevar.py":"b5669934eddaf5b9cc0c27b966e2566b5f669f1c5a345f005960930fb499097e","meta-python/cdsl/xform.py":"bf200b711570b905f0fb30611ba64f131f98593060eedbe5575054cca4928593","meta-python/check.sh":"707cda14534882e8d4593f93c056390560394c9bc8a687e99f10a56d7b09cb92","meta-python/constant_hash.py":"c752e6dadf3a9a5bd00c978e85ab27a20c49138a1ccdc6fc9a1904797a4bfe48","meta-python/gen_binemit.py":"76472fb199a330b934ba9ad0a1bbacfb52f0eae7c9a66d83f0d7890970323a2d","meta-python/gen_build_deps.py":"53bbdd591c91f8e28da95e4da02c9299a01a32d8be6a0c7490b6b7c60a4fbebe","meta-python/gen_encoding.py":"471c5f4ecf24a2a173864c62a48920e5fbe1aacd2229b3eb4483637570bb26b2","meta-python/gen_legalizer.py":"758f5aa072be71041915b8b662a59fc2dab8b0359dd91533161a8f449051fb3b","meta-python/gen_settings.py":"f13e47335ae87b6381134b3d334f2fcbdfc03da92a8864dd1ff1c026408062a7","meta-python/isa/__init__.py":"e499c1206cd095a926fa0ca7eb9d0a50a802ed71c8eb7598e5d3a0f939c8ada5","meta-python/isa/arm32/__init__.py":"eecba73231aa398ded7304690bdba3450dc163afd4360f1b0ad02a28e2380363","meta-python/isa/arm32/defs.py":"01c41dbd7406c624e26229df6befa0992194bddcc7d11e8f6174abfe2b33bf61","meta-python/isa/arm32/registers.py":"c03ca6435828ad5f262049e42f1f71bcf74903831f85daa92c3f322a6c1050ea","meta-python/isa/arm32/settings.py":"afd5a04a9d029f578d6f62dc7c539191886cc9f9dea15d65fc66bf37a63b8814","meta-python/isa/arm64/__init__.py":"f6877253cf786d7ee972881e7d9b3c78c11e6b024e4e227487340dd01d0c44e4","meta-python/isa/arm64/defs.py":"797c5bb6d11fc7a44afe67476136dbd11c40f5e13a1c8f52f9f96be4441677b2","meta-python/isa/arm64/registers.py":"9bdd06edaa382be96042e1ac36d63137e73292292b61dcf4becb7d1428130317","meta-python/isa/arm64/settings.py":"f7b1f8733e775ea8005372ee35f1c2a627b3a69d722e837295599e4cf1f5eb43","meta-python/isa/riscv/__init__.py":"c11607c9eef0bc2707daa3edd4174e934c7a0dcc8ce90cee2c9292a85b1ac596","meta-python/isa/riscv/defs.py":"e73740055c4fb123c45453fc149a807e9720466de848022d5375049bdcfc311c","meta-python/isa/riscv/encodings.py":"ecaad5ea98273ade1cb10606354e893342c495bb48771df50121f789566d7be6","meta-python/isa/riscv/recipes.py":"3852e5b7aa6995fa721ba91744a0470343ce1834651e7b9cc97b5d69af7dfdc5","meta-python/isa/riscv/registers.py":"ef9aca3a6ec2b08ee8f5952186d232861b64a919b671b41911a365e7672b01bd","meta-python/isa/riscv/settings.py":"dfe29722d67be0620a70e08cfb802829a26f5fd339a9342a8ac2dd419daf8a85","meta-python/isa/x86/__init__.py":"ad579de68ea7bf5dc2bce0e3a6f09e7978b1697f1afec8a5ce5dc591136e590d","meta-python/isa/x86/defs.py":"b5eb7889b6f5e5b221ed3923d0137bbb1566c55b5961448cc39e4ea2f13cf4b7","meta-python/isa/x86/encodings.py":"028acd4ba348ba2dfa601217b8d4597748f87cdbfbaeece477150fc09bab50a6","meta-python/isa/x86/instructions.py":"530cde78e6b9f6e4ea2192985f4c5c77a987cdc19001d50fb47fa8e36a62f52e","meta-python/isa/x86/legalize.py":"1375ded072c29459e7c0e40ecb02f28d5395d9d8c603eb70e338b2bf2991c9cd","meta-python/isa/x86/recipes.py":"c9d7910cd4311ee4307460ca55070404e212727814664f2a2096b9b702ff7178","meta-python/isa/x86/registers.py":"ff934491d07ec6b51fbfd454b865a7c7c191ffbd31b1804615735266b120f4b2","meta-python/isa/x86/settings.py":"d779a768475cf00c2a8d3ddb5cd0a70ce34662e0ebb52ee26a7e1a495ec41aa2","meta-python/mypy.ini":"5ec2f7cc0bbc4fd0435643d6b72e715bd9568a3a0fe14c043f9e559c405b66fb","meta-python/semantics/__init__.py":"c9dd72fde0ab020d3328dde532d92b39e438e4147930e41df7613c27727e11eb","meta-python/semantics/elaborate.py":"3a3fbba83a6818c2d1ce236fd0413111380875a0307f7a5f4b5dd66d8ef714b1","meta-python/semantics/macros.py":"b218c52e1bd4f019dc14a27d315b4f3405a10e5bdc6f2523fe709c8faf91b418","meta-python/semantics/primitives.py":"4e5eb0c90fcc295686732c8c66ad7a793997645c9a676c97babf06823fd2b60d","meta-python/semantics/smtlib.py":"48ef80320f21682860bbf5f79f18739f1d10f0b1fe581ebb05541e90dc2f2f4f","meta-python/semantics/test_elaborate.py":"3a4c850a7385007422c7549661b211903cd1dd1606dad7a86262ae27e697bca6","meta-python/srcgen.py":"999557d683e808a2ca90688c489ec4aff65798f44ac321ecf7de34d307261913","meta-python/stubs/z3/__init__.pyi":"6aaeb80f783b29c4364dee21da45f6df041c0a4807189a15777ee5447f6515dc","meta-python/stubs/z3/z3core.pyi":"c01a41d468e07cc4f8b405c292ed7f8c82bc1077f8b82dfde1e474577ade3335","meta-python/stubs/z3/z3types.pyi":"30009c951af99b9028d47cd4cabae95ff9742b77b690bd8dd63f6b7dba580759","meta-python/test_constant_hash.py":"157cf4f8964e0f04c041ffd349e889ce565b144453436690578c5d03c3a60216","meta-python/test_gen_legalizer.py":"d7da85622f142d2d66d0b92660b6f04b1424788bac05e6fbe0e5822b54dec705","meta-python/test_srcgen.py":"d6d7775e19a5b2621360c00eb6d92dfcb4568e49220993e0ceaac9628dbfd661","meta-python/unique_table.py":"5bd500667430c15f6ae586603d8612fb3bda07b072e40d86286e08392bdc3127","src/abi.rs":"76ee030cf0780fe63ccbf855b1161f215e3e2991cb9abe71ca9aff25e5f1dbc2","src/binemit/memorysink.rs":"ad79459de45431b04f28a296074d3613e804538911fbfd451b68058218594574","src/binemit/mod.rs":"bfd83cb1e23e7b2f6926134e1e57548af240036128033f19e557f4d130133e87","src/binemit/relaxation.rs":"95442e08349762b11dce3d8f5d86adea97d3554a0353d7d519bbabfe18a87b01","src/binemit/shrink.rs":"45434d5fb17804f5199f5fa80fd96aedaeac1ca3824766236eb16b6b529155b4","src/bitset.rs":"d57a79058a31b094b4bbe9d34876a5543286df1e08b5ceadfd05a9efc5a3b1ce","src/cfg_printer.rs":"69b4f16132c886ef0b883c8b78b59d041ceec3c8b96dd8015e990ac06733ce91","src/constant_hash.rs":"330e8289789ee351d0abeaf4b5e859b8db8772306e0820d34864fc9905d53b38","src/context.rs":"5c5eeb2aa36eec4d311efb60f6135fbeb223382e17a6e4126a779dfaf73e9be7","src/cursor.rs":"dcce946ad85d8fc2f1c9cc885ae8a0440c37d9e512606fb8a518aaffcc6d6f8f","src/dbg.rs":"1898d94cff0975815eb348651702e95c8f2f63886501d3b7043ee75668480472","src/dce.rs":"d8ab7c0cac0416f9d75afdacc60ba02e532361ec927c3f8bd171b1a53ec8b31a","src/divconst_magic_numbers.rs":"baccf8894bf8113b25fe3c35a16160544557d5a77290e08b27cc50f258973532","src/dominator_tree.rs":"7ee4114026011b11d49e48c5a9202970bafe3b22c2074f7c1390b98ebb2edb7a","src/flowgraph.rs":"bf520026c32c5454554d0b078b64a78bd44f3c0f4f198eddf71bcfd78cc963a3","src/fx.rs":"8a5d07487906d8316a179e826fcd817a92a4860686256a6fd9d78ba47c63f330","src/ir/builder.rs":"1a12c57f4168fa3c2fcf6be2f19d0d377eee40f24ae4390e6ba9d954f85a8a3e","src/ir/condcodes.rs":"5247c8d849e1372d2a22379c33a4a88226ec6187be18ca6f2c4e0f315d812aa8","src/ir/dfg.rs":"50a3a7c44b6a993f033a65682beaeae12d5ed78ff67d7ac5e205e645ef8e122f","src/ir/entities.rs":"8bae1166b59afd38953e7d9154ae141c979ab77153b9512f45db7b82a256fdf4","src/ir/extfunc.rs":"9806734eeb480724481128d8c1de78a3b1f80f1214c20f24131196a0df137872","src/ir/extname.rs":"ed2c0b52cdaecc7f0ba9a894ef9fffe139e09b520e43dcd6f0c887a3d41a31ac","src/ir/function.rs":"907c4658865559c27fe4ee2f4064d0c64add712f2a190185236dba3cb98d4c32","src/ir/globalvalue.rs":"1f6125788a9a5c118716f87fd72e85009d2e0a7f4c13b2a7904be8c7412ee4eb","src/ir/heap.rs":"a59d3e5901412b53c0b53a8cdf10765ff5921de9c410ae9acea226c89827df3c","src/ir/immediates.rs":"029a8aa905e6fd0f3334e17a28e73ac36684df2ca0829dcae0158ea8cf64ca8c","src/ir/instructions.rs":"2180de1d127ab5878c5743d8e096d551edb973e0ff1d373c4239c32e7981364c","src/ir/jumptable.rs":"7764abc9aa027a5a89d22059b360372bd9a19686887c5a7830f7637d6f188e1e","src/ir/layout.rs":"bb45eefde16ac9423f637dfcc2796ae7b955a97f38a55f23a19cc45da5decce1","src/ir/libcall.rs":"55fd77f6e32370812a271f4fd5d9817c03904733be79d49e17e2683fe516e30e","src/ir/memflags.rs":"dbcf3798ab66dc764b73fb7f139a621c54cc6bcc683f1f70a33ed7e8c3486bfd","src/ir/mod.rs":"ec1da8a8bd99a3dbbcea59e2c10aa60511488471930646ab293f47d428013224","src/ir/progpoint.rs":"49433f22bd6ff3a96ad0733ff612f3617b312e4492b6b663187141966f6aa701","src/ir/sourceloc.rs":"37ef5fd8cef1de99620797d7d5aba3630e737171853c8471495c685dafac19b6","src/ir/stackslot.rs":"2f54359339837bb1d0d817d3af21bb4b1b050c31703885dfaced29f6e41153c2","src/ir/table.rs":"dcc3b663a989b2b084402b08dc9a0e928dbd052e194a46a1886cc6f0cf1a5f2c","src/ir/trapcode.rs":"59e223193617b8c1043ddd3a907c6131f2987e8fe0965ebfd9f7c056c064b7c5","src/ir/types.rs":"1f644beba2c797f7bd89378d930c3eca1491ec5f82c4715945ac63a0a5754473","src/ir/valueloc.rs":"4c676c2d21d75611ef922a230ee82846afb565ce0f55bc71e33c70e1a1d92a07","src/isa/arm32/abi.rs":"74775c5f1eb95764e46815fa8b31891416a616fdd212972eb77aead43b3345a9","src/isa/arm32/binemit.rs":"52b2f4b3c6d8683ed6963c1f53907ac57b6aab7cc149464c9a12d6875aa3b5c6","src/isa/arm32/enc_tables.rs":"b5b7f2fdcaf1d1878a357c54ab757e94f0d06e5bd391ac5d876e96f5b30d6b7a","src/isa/arm32/mod.rs":"24492cfe9120a3ebb199b1d0a0b58425c58970bae32349b3cf2dd390ce51e62f","src/isa/arm32/registers.rs":"254b568a02480f46bb4967a24a438390231014258f0c159f0a41dbafe8e66d56","src/isa/arm32/settings.rs":"188f3cc445168b472b488e73b011e83edd31ac17e3841dacda07a55ccf923468","src/isa/arm64/abi.rs":"52353ed2e2133dacddaad70a876ecebb9c179c19b911ffa823b5b89d3ee7a17c","src/isa/arm64/binemit.rs":"4465cceb68d03ae4d0fdf188c9b86870fb57b3617d67f0bb7d476e5afb581e81","src/isa/arm64/enc_tables.rs":"8c829c544daeed9adc8458891614a0be6f149e775bd22651465f2c165d4a9e56","src/isa/arm64/mod.rs":"f9ca60e7407b69595cb4ef42103ed079e7bcb40546f11d944ddcfc6a04a7fd11","src/isa/arm64/registers.rs":"308cfcfd9ff2191d7656e7350bb36e41803664eb86ae490fb4b4d3549b25b6a2","src/isa/arm64/settings.rs":"0dff47e995e5d9740deb0116b59e91fbcb725a7fa1bdbd802bf83c13381b17f7","src/isa/call_conv.rs":"833ac811ff78ab8d3a5052165e76c51c6da7686020d95462c18074750fb790ed","src/isa/constraints.rs":"f2e2dee4308dabaab1071983d2edd9a9972a99c5c99edf919adbb4554b4eb067","src/isa/enc_tables.rs":"3497f3d701f21d6f952424abf31515fde9e67aea1cde26236c9ee8b033c61ae6","src/isa/encoding.rs":"7ea5b4400530172f96e263561682886ea6c67e706398d44a83933ef7f0ac98a5","src/isa/mod.rs":"40ab1c6d86f903a88bc7ef83b7cdcbee0eedbfdf7112fb7a6749c0c8cc9ee42c","src/isa/registers.rs":"4a91d4888df5eeed1802f34c43a42b82aaf1f9928a58329b0cbc9c3c57c75485","src/isa/riscv/abi.rs":"36557b91ad16a1344c80fbb16a62b46eac88500d76cb9ebcd4eae224dd67b2de","src/isa/riscv/binemit.rs":"0bd76b005b53b71bdb59057a20671fbcd8ab1c37d767bfd4ab0a92d05e192d9a","src/isa/riscv/enc_tables.rs":"ab73c80fef6b1256fbd3c0e1bdd8e43a20f7d132a32236f6bfc028e9003adfe0","src/isa/riscv/mod.rs":"377dfc7dc9940d284b21bf3d2433916dd9c3df79cce172a2a75ef572dcafe98f","src/isa/riscv/registers.rs":"666c2abe1a93db5f1573d1603db6c13c37f3fc877c0c93b64d1b971921bfa950","src/isa/riscv/settings.rs":"d018d23418ff3fb98571bcdeeddde3277bf213b4b8ac1e55b6573e128b3931ce","src/isa/stack.rs":"d023c57feb944c94c4dc8b7138dcbc9d37ff18ca23c23d83e9bab3c88299ffa0","src/isa/x86/abi.rs":"98092944de6b8a8171f1c7d24a63d23ee6dc77a5b4048be113582c5362ac0158","src/isa/x86/binemit.rs":"328f5803717e095abbda0d0a49a79b8f5250a6d16a739a5912ee0a71f276d9de","src/isa/x86/enc_tables.rs":"250ab677b2a316f9826495a0719f71f4b54b3c3c26d0bb42a76dd85b55a2f8e3","src/isa/x86/mod.rs":"15488d60a950aa4cb75afb63d42d4524e1fcea3b77c7c160e2cb862ec2236a92","src/isa/x86/registers.rs":"bed70bbe1f56f3ef03ea7cd1bea68eb911913cb4c8b93167e044dfc639f7f461","src/isa/x86/settings.rs":"cd8f8c5255663f6e247f0634088b16b53d785ee8c62cb5c0926b3d27597c12ff","src/iterators.rs":"f85f52d3fa707a0eb974c92215b3e976923ce8f9481219f7812e0f2869c2bd37","src/legalizer/boundary.rs":"435707e84b5e406843238d27f5cc4886f4ae37e2c4d25a749e189a378b3af632","src/legalizer/call.rs":"6a82a9daeba5f452a7db0c47dcc43829b119b0652961bc2aa336b809c3f34ef1","src/legalizer/globalvalue.rs":"896f59dc5b8c4a5aee45e3f59211066f214d3ab9142865d80ad6610633363ac9","src/legalizer/heap.rs":"4caf9b5f98556364f16164613872c29a457dcbf44b07cc49fc415abbcd61ec6c","src/legalizer/libcall.rs":"6e58da5e1c2419192a3393debc6e9714df9728e59e1740ff569e8a9c0daa40d5","src/legalizer/mod.rs":"7df6ed89a80c9dab7f6e3ddbedcb1c4bcc86c3c6e5bdc3e71e51d5322cb1ce52","src/legalizer/split.rs":"13fe4d2cecea166ecdc1ebb11f5254374ee170518f1a61de7ac0a921bc8fb25d","src/legalizer/table.rs":"d6e09f8340ca597fdb13f86021e5c53bd3161dc4258effc56c1f6d9be7b819ec","src/lib.rs":"12855b9aedb583859a89dd8f8a7e1ff6d6c9f68e938f60ff4d123aa3d98c2da5","src/licm.rs":"a4b482c995daf0ecf928a525747316760986a42234331554ae68fe9ef8c7145e","src/loop_analysis.rs":"58fc3cc0e700f05e3131ff1b16ff975d4f32a68c790f095d8445bd300356d3c0","src/nan_canonicalization.rs":"9619bb5554791bd1be75ecd98564d6c9f5b65132bc07c5c4d8c210cd79b66f82","src/partition_slice.rs":"bc13504e7658aab565918d965a0b67e941eb572e583870571bc6dbb2b9aad272","src/postopt.rs":"f3af3f27496de14e4e65b7efb0748ca11edff8fedd7b57d680b310de5156aa62","src/predicates.rs":"44bbc09ae0c7d5b54c05eb6061b062e92d1893edc7dda43ae2bccfedc6bb25e3","src/print_errors.rs":"0955151433563b4c305c78b9a8f192c5fe0556ac2528a5ede3b2fd4d143eb743","src/ref_slice.rs":"421a61323c11858a596d50220487f399e1bcedeff0e8d1b716dd4b3531eb01a5","src/regalloc/affinity.rs":"66ee6b9789ec207393c318b14177e1439a54f197b13ebefdb0c4ab77acf38c00","src/regalloc/coalescing.rs":"881c8be32eb4d4b34cf208d0dba3e18b8469bc19f19aa7120514c801562392d3","src/regalloc/coloring.rs":"72bef8b5e3425c805d62cf2329acd29510f1be0886ee73308b69938cf2de203f","src/regalloc/context.rs":"a7da41171ad73cd541977657f0af0d5308567fff140fa7eb7ee44e03c62d8c96","src/regalloc/diversion.rs":"d46d733f6d00a8f536d5c7c8b8fc6f348c3d0605dd0ee77e1d8359367ba53347","src/regalloc/live_value_tracker.rs":"28823003dc72e8a4702776a8ab5ffd878712700a272b64376b0de2022e0ee31a","src/regalloc/liveness.rs":"cc904b813a8a2fc819f0cd5ffacb5a4b82f29ae3ae6a34cccc01fc47c20d150f","src/regalloc/liverange.rs":"7a28454e5f70d570db439b966a01ead759b65eb65c5845f9c58bf2f230a5f2ab","src/regalloc/mod.rs":"6254df639f9289fd578e01b7dca99bc9c9e3c6680c6d031405e8df8d0cff31ad","src/regalloc/pressure.rs":"c9d1098266c5b8b89eff84a55be3bc4838b70416c2446edf57f5257a6232d059","src/regalloc/register_set.rs":"bc58f93f22f0adbe43260fe20c6089be1fca64f5bcc4acff85dc0a5ec5b61937","src/regalloc/reload.rs":"4dd64aacc97fda5e950222df37df20bf2281cfbca93d1945f3a4f25e4b867aac","src/regalloc/solver.rs":"e2a6d2782d213676dec9781aeb5a37806e092d6daac3249a6cd4d4c92a9d117f","src/regalloc/spilling.rs":"f21ae054e6546b0cd774a94bb301517ba341b985223b6db36e6c9ba995eecfd8","src/regalloc/virtregs.rs":"e5c8da6860ba9495f9396621530347e1dd6fc5b2fae2eb23c171ea77429356f1","src/result.rs":"c10354d615f93caa446c3c8c49d6ba3af762816af470f9c4accf04315cce9753","src/scoped_hash_map.rs":"5afafb3a4039094c3a2aad1582354220d21f399aa50046e7f4a1259e1976597e","src/settings.rs":"23eb1461f7f02f4e26cca71ddc72ead7887bb1aec64b04eb6eb0397c402bf3c8","src/simple_gvn.rs":"c8feb380d4831badc59aa1e65efeafa6702711585817fe5f6b31de6b265fac24","src/simple_preopt.rs":"3e8958c7eddf0f0e207d1f6a10ce9d9595b554dcabd984fcea1d6a4716365358","src/stack_layout.rs":"c5de271e296fc424f1a30017620bc88500369c8e553fef6e95beccb9c6640e7c","src/timing.rs":"a6808943eec68f5d3ff32132d40c07c142e6aa8073473561573a013978883e4f","src/topo_order.rs":"b01ed68a7300691f41ac434e58a5267b10a8b4a7056d65035e24aa8a6722122a","src/unreachable_code.rs":"40cc71a02887ee4065c76ce96dda0a363a8cc134ec784fe5ed1f276db76596ce","src/verifier/cssa.rs":"e3e1d77b763c0ba82d3b59ab5b4667fd3152d5a08be50b58b0c82f86376bb062","src/verifier/flags.rs":"f4ba0e0c13fd643bdbec6466219a25a33993a6e170debb48497a859d9f79d914","src/verifier/liveness.rs":"2631037bafa88659bc47d2174e261f5acb1702ca522722a597fa28e474994d79","src/verifier/locations.rs":"9623bbc2d2f86f36893eebe60330fd51b99c9f9c8e5162c61cc89ab221e75b5a","src/verifier/mod.rs":"0d4a46879a8c4b110b354ba35c667478a785e69b8a1c3b97c7f1aadc79433637","src/write.rs":"24b5caa5fa9145f8da78b9ef2342f52a05d04318b3b03f5b8bb1d486a3d46041"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"9e1e9d197a2fcdb9bfd3c32a7608ab772ed4301aa0b9f2e60b73c98f542ac760","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"7c0919d12ff966e0c400e7cfa0a555b2e50ede20c0dfb4f4401e35a828134ea9","meta-python/base/__init__.py":"4fac8bb055541dc964383bb04223bae6dfbfe348abf5e23f83655966cbb4aa8f","meta-python/base/entities.py":"f956b8ba863cd26f6867e13c1b2db3c202e9b07c48c1e43a9c2b41ad0deb8922","meta-python/base/formats.py":"f9fb41210bc1f99a78cb7a60ee9f01c603412c6b1db7e69abbcc0f573cf9fb40","meta-python/base/immediates.py":"f42682d86bda7b569ec2fc2debd9036355999e61caaa9fbf8307e0be8a164814","meta-python/base/instructions.py":"4f92066dc9cfa651e395afc8956b1a082b4e8591c6f7310db89593ff55322e2b","meta-python/base/legalize.py":"8455a5c7428e98d23e432d7be28cc863804781013ac614ba57f6d231cd810d28","meta-python/base/predicates.py":"53a5a9a37021a9762f0faec9f9e3c5b713411d47dd9192423cfe0f7a61916809","meta-python/base/semantics.py":"b90cbca9a143676ac37496c83634a66a83360248e4505ff7bda12b0d454efd92","meta-python/base/settings.py":"97fca9ddaab347f50f1594c93283f8f474e53a7232eae65949f56a6a702c2bba","meta-python/base/types.py":"9616d6fe4cab050827ab02eeb9843eacebbb8f7555521f504b5ee2ddf7214fdb","meta-python/build.py":"254a7fb51f6025c54f5b08135b83ffadfc2e6c02cb8c3078fdd06c0eff4b91da","meta-python/cdsl/__init__.py":"b534ec129a0e517f0d13313c2b4bb3a345d3e5b62693a31d8d42c80a219e39fa","meta-python/cdsl/ast.py":"8509f15eaaaf2fed2bec93eba19d313d37c2ddd70d1399a491a394206cbfabe4","meta-python/cdsl/formats.py":"fedfeaec754b40d6a9cc92b827976782c615d8eab1c7f47f6b47510cbef585f4","meta-python/cdsl/instructions.py":"50fe9329a56e6294a27306aad7be22c88a5b5ef42c3b030ec59218167157f62f","meta-python/cdsl/isa.py":"dc530a4dd5642e3379398dfc8a90ad6ae1692302de63851370bdfa8abf4cdce0","meta-python/cdsl/operands.py":"e24914eae4059b88781bf5a5d7a14ecf98b10a701ed6cf6e88d15981b2ccbfdf","meta-python/cdsl/predicates.py":"fe2dc6e78ff6d4bcb6b1d5448ded343ede0f03b96a965a11aef0f5bbe7bb8f56","meta-python/cdsl/registers.py":"939dafd1b8976a6cd456c9a5e4b374af81fafb9da979ea3a1f6d14e4645914a6","meta-python/cdsl/settings.py":"400d853a815f64c0294d0c0e03d3c514942760faa8b06152b1078a92ddb9f62c","meta-python/cdsl/test_ast.py":"947e934e2862445a158bf266dff58a8c88aae31fb34a7f823309ee58a15c5393","meta-python/cdsl/test_package.py":"ffa53d20e023ecb89137294bb13614f4de9b09e1bf05d9772131570bf78f7987","meta-python/cdsl/test_ti.py":"04656e2da99ec37875f91172bb2fa423da1ee27bf523a4d41af311ea22492d1b","meta-python/cdsl/test_typevar.py":"768bf3c35481950264ad6fd7059a532ac62b121ac439983bb4fe1ae76a1d1248","meta-python/cdsl/test_xform.py":"ddb6633c7941bbf68570701cb887a81d6b4b27f4bc45eabccf2ce287ad9b77e9","meta-python/cdsl/ti.py":"566e91f0bb3ebacd5abf9f5a663caee69706c2e829dee1ed735d229198689f76","meta-python/cdsl/types.py":"adee4bbc1a9478288fa4b53ee1edeb5ee3296dba2c70bfbe01e923895064999e","meta-python/cdsl/typevar.py":"b5669934eddaf5b9cc0c27b966e2566b5f669f1c5a345f005960930fb499097e","meta-python/cdsl/xform.py":"bf200b711570b905f0fb30611ba64f131f98593060eedbe5575054cca4928593","meta-python/check.sh":"707cda14534882e8d4593f93c056390560394c9bc8a687e99f10a56d7b09cb92","meta-python/constant_hash.py":"c752e6dadf3a9a5bd00c978e85ab27a20c49138a1ccdc6fc9a1904797a4bfe48","meta-python/gen_binemit.py":"76472fb199a330b934ba9ad0a1bbacfb52f0eae7c9a66d83f0d7890970323a2d","meta-python/gen_build_deps.py":"53bbdd591c91f8e28da95e4da02c9299a01a32d8be6a0c7490b6b7c60a4fbebe","meta-python/gen_encoding.py":"471c5f4ecf24a2a173864c62a48920e5fbe1aacd2229b3eb4483637570bb26b2","meta-python/isa/__init__.py":"e499c1206cd095a926fa0ca7eb9d0a50a802ed71c8eb7598e5d3a0f939c8ada5","meta-python/isa/arm32/__init__.py":"eecba73231aa398ded7304690bdba3450dc163afd4360f1b0ad02a28e2380363","meta-python/isa/arm32/defs.py":"01c41dbd7406c624e26229df6befa0992194bddcc7d11e8f6174abfe2b33bf61","meta-python/isa/arm32/registers.py":"c03ca6435828ad5f262049e42f1f71bcf74903831f85daa92c3f322a6c1050ea","meta-python/isa/arm32/settings.py":"afd5a04a9d029f578d6f62dc7c539191886cc9f9dea15d65fc66bf37a63b8814","meta-python/isa/arm64/__init__.py":"f6877253cf786d7ee972881e7d9b3c78c11e6b024e4e227487340dd01d0c44e4","meta-python/isa/arm64/defs.py":"797c5bb6d11fc7a44afe67476136dbd11c40f5e13a1c8f52f9f96be4441677b2","meta-python/isa/arm64/registers.py":"9bdd06edaa382be96042e1ac36d63137e73292292b61dcf4becb7d1428130317","meta-python/isa/arm64/settings.py":"f7b1f8733e775ea8005372ee35f1c2a627b3a69d722e837295599e4cf1f5eb43","meta-python/isa/riscv/__init__.py":"c11607c9eef0bc2707daa3edd4174e934c7a0dcc8ce90cee2c9292a85b1ac596","meta-python/isa/riscv/defs.py":"e73740055c4fb123c45453fc149a807e9720466de848022d5375049bdcfc311c","meta-python/isa/riscv/encodings.py":"ecaad5ea98273ade1cb10606354e893342c495bb48771df50121f789566d7be6","meta-python/isa/riscv/recipes.py":"3852e5b7aa6995fa721ba91744a0470343ce1834651e7b9cc97b5d69af7dfdc5","meta-python/isa/riscv/registers.py":"ef9aca3a6ec2b08ee8f5952186d232861b64a919b671b41911a365e7672b01bd","meta-python/isa/riscv/settings.py":"dfe29722d67be0620a70e08cfb802829a26f5fd339a9342a8ac2dd419daf8a85","meta-python/isa/x86/__init__.py":"ad579de68ea7bf5dc2bce0e3a6f09e7978b1697f1afec8a5ce5dc591136e590d","meta-python/isa/x86/defs.py":"b5eb7889b6f5e5b221ed3923d0137bbb1566c55b5961448cc39e4ea2f13cf4b7","meta-python/isa/x86/encodings.py":"af0a811fccef5c8a807156369cdc1a5cbc4bef90c753e721d49b3ccc89d34325","meta-python/isa/x86/instructions.py":"530cde78e6b9f6e4ea2192985f4c5c77a987cdc19001d50fb47fa8e36a62f52e","meta-python/isa/x86/legalize.py":"1375ded072c29459e7c0e40ecb02f28d5395d9d8c603eb70e338b2bf2991c9cd","meta-python/isa/x86/recipes.py":"c9d7910cd4311ee4307460ca55070404e212727814664f2a2096b9b702ff7178","meta-python/isa/x86/registers.py":"ff934491d07ec6b51fbfd454b865a7c7c191ffbd31b1804615735266b120f4b2","meta-python/isa/x86/settings.py":"7ef2e43521b9b11e90a93c1c60946e65d420ecf6e15ba17f933c7a24a5124e5b","meta-python/mypy.ini":"5ec2f7cc0bbc4fd0435643d6b72e715bd9568a3a0fe14c043f9e559c405b66fb","meta-python/semantics/__init__.py":"c9dd72fde0ab020d3328dde532d92b39e438e4147930e41df7613c27727e11eb","meta-python/semantics/elaborate.py":"3a3fbba83a6818c2d1ce236fd0413111380875a0307f7a5f4b5dd66d8ef714b1","meta-python/semantics/macros.py":"b218c52e1bd4f019dc14a27d315b4f3405a10e5bdc6f2523fe709c8faf91b418","meta-python/semantics/primitives.py":"4e5eb0c90fcc295686732c8c66ad7a793997645c9a676c97babf06823fd2b60d","meta-python/semantics/smtlib.py":"48ef80320f21682860bbf5f79f18739f1d10f0b1fe581ebb05541e90dc2f2f4f","meta-python/semantics/test_elaborate.py":"3a4c850a7385007422c7549661b211903cd1dd1606dad7a86262ae27e697bca6","meta-python/srcgen.py":"999557d683e808a2ca90688c489ec4aff65798f44ac321ecf7de34d307261913","meta-python/stubs/z3/__init__.pyi":"6aaeb80f783b29c4364dee21da45f6df041c0a4807189a15777ee5447f6515dc","meta-python/stubs/z3/z3core.pyi":"c01a41d468e07cc4f8b405c292ed7f8c82bc1077f8b82dfde1e474577ade3335","meta-python/stubs/z3/z3types.pyi":"30009c951af99b9028d47cd4cabae95ff9742b77b690bd8dd63f6b7dba580759","meta-python/test_constant_hash.py":"157cf4f8964e0f04c041ffd349e889ce565b144453436690578c5d03c3a60216","meta-python/test_srcgen.py":"d6d7775e19a5b2621360c00eb6d92dfcb4568e49220993e0ceaac9628dbfd661","meta-python/unique_table.py":"5bd500667430c15f6ae586603d8612fb3bda07b072e40d86286e08392bdc3127","src/abi.rs":"76ee030cf0780fe63ccbf855b1161f215e3e2991cb9abe71ca9aff25e5f1dbc2","src/binemit/memorysink.rs":"ad79459de45431b04f28a296074d3613e804538911fbfd451b68058218594574","src/binemit/mod.rs":"bfd83cb1e23e7b2f6926134e1e57548af240036128033f19e557f4d130133e87","src/binemit/relaxation.rs":"95442e08349762b11dce3d8f5d86adea97d3554a0353d7d519bbabfe18a87b01","src/binemit/shrink.rs":"45434d5fb17804f5199f5fa80fd96aedaeac1ca3824766236eb16b6b529155b4","src/bitset.rs":"d57a79058a31b094b4bbe9d34876a5543286df1e08b5ceadfd05a9efc5a3b1ce","src/cfg_printer.rs":"69b4f16132c886ef0b883c8b78b59d041ceec3c8b96dd8015e990ac06733ce91","src/constant_hash.rs":"330e8289789ee351d0abeaf4b5e859b8db8772306e0820d34864fc9905d53b38","src/context.rs":"5af7877cb66d004aab1f1a3a01ef74a4cb3758071a3fa69d56dac3c1a559609c","src/cursor.rs":"dcce946ad85d8fc2f1c9cc885ae8a0440c37d9e512606fb8a518aaffcc6d6f8f","src/dbg.rs":"1898d94cff0975815eb348651702e95c8f2f63886501d3b7043ee75668480472","src/dce.rs":"d8ab7c0cac0416f9d75afdacc60ba02e532361ec927c3f8bd171b1a53ec8b31a","src/divconst_magic_numbers.rs":"192f7cb7a9a844f975dfdac49fa659e9c51c3e62ca7b8edfe2b55cec8c2dd61c","src/dominator_tree.rs":"7ee4114026011b11d49e48c5a9202970bafe3b22c2074f7c1390b98ebb2edb7a","src/flowgraph.rs":"bf520026c32c5454554d0b078b64a78bd44f3c0f4f198eddf71bcfd78cc963a3","src/fx.rs":"8a5d07487906d8316a179e826fcd817a92a4860686256a6fd9d78ba47c63f330","src/ir/builder.rs":"1a12c57f4168fa3c2fcf6be2f19d0d377eee40f24ae4390e6ba9d954f85a8a3e","src/ir/condcodes.rs":"5247c8d849e1372d2a22379c33a4a88226ec6187be18ca6f2c4e0f315d812aa8","src/ir/dfg.rs":"f5cb672656c7e56185544c3ff7f60f8a4b7b9f172d9a9920144616bc165d5928","src/ir/entities.rs":"8bae1166b59afd38953e7d9154ae141c979ab77153b9512f45db7b82a256fdf4","src/ir/extfunc.rs":"9806734eeb480724481128d8c1de78a3b1f80f1214c20f24131196a0df137872","src/ir/extname.rs":"ed2c0b52cdaecc7f0ba9a894ef9fffe139e09b520e43dcd6f0c887a3d41a31ac","src/ir/function.rs":"5d8dcd835b84bbcf000ca772bb5466ffa5e3d988167097e60b89119cafe46266","src/ir/globalvalue.rs":"1f6125788a9a5c118716f87fd72e85009d2e0a7f4c13b2a7904be8c7412ee4eb","src/ir/heap.rs":"a59d3e5901412b53c0b53a8cdf10765ff5921de9c410ae9acea226c89827df3c","src/ir/immediates.rs":"029a8aa905e6fd0f3334e17a28e73ac36684df2ca0829dcae0158ea8cf64ca8c","src/ir/instructions.rs":"2180de1d127ab5878c5743d8e096d551edb973e0ff1d373c4239c32e7981364c","src/ir/jumptable.rs":"7764abc9aa027a5a89d22059b360372bd9a19686887c5a7830f7637d6f188e1e","src/ir/layout.rs":"bb45eefde16ac9423f637dfcc2796ae7b955a97f38a55f23a19cc45da5decce1","src/ir/libcall.rs":"55fd77f6e32370812a271f4fd5d9817c03904733be79d49e17e2683fe516e30e","src/ir/memflags.rs":"dbcf3798ab66dc764b73fb7f139a621c54cc6bcc683f1f70a33ed7e8c3486bfd","src/ir/mod.rs":"37685305acb21dc24896e5d7977784988fe723c09bdf8ab8cc7830ccabd76a11","src/ir/progpoint.rs":"49433f22bd6ff3a96ad0733ff612f3617b312e4492b6b663187141966f6aa701","src/ir/sourceloc.rs":"37ef5fd8cef1de99620797d7d5aba3630e737171853c8471495c685dafac19b6","src/ir/stackslot.rs":"2f54359339837bb1d0d817d3af21bb4b1b050c31703885dfaced29f6e41153c2","src/ir/table.rs":"dcc3b663a989b2b084402b08dc9a0e928dbd052e194a46a1886cc6f0cf1a5f2c","src/ir/trapcode.rs":"59e223193617b8c1043ddd3a907c6131f2987e8fe0965ebfd9f7c056c064b7c5","src/ir/types.rs":"1f644beba2c797f7bd89378d930c3eca1491ec5f82c4715945ac63a0a5754473","src/ir/valueloc.rs":"4c676c2d21d75611ef922a230ee82846afb565ce0f55bc71e33c70e1a1d92a07","src/isa/arm32/abi.rs":"74775c5f1eb95764e46815fa8b31891416a616fdd212972eb77aead43b3345a9","src/isa/arm32/binemit.rs":"52b2f4b3c6d8683ed6963c1f53907ac57b6aab7cc149464c9a12d6875aa3b5c6","src/isa/arm32/enc_tables.rs":"b5b7f2fdcaf1d1878a357c54ab757e94f0d06e5bd391ac5d876e96f5b30d6b7a","src/isa/arm32/mod.rs":"24492cfe9120a3ebb199b1d0a0b58425c58970bae32349b3cf2dd390ce51e62f","src/isa/arm32/registers.rs":"254b568a02480f46bb4967a24a438390231014258f0c159f0a41dbafe8e66d56","src/isa/arm32/settings.rs":"188f3cc445168b472b488e73b011e83edd31ac17e3841dacda07a55ccf923468","src/isa/arm64/abi.rs":"52353ed2e2133dacddaad70a876ecebb9c179c19b911ffa823b5b89d3ee7a17c","src/isa/arm64/binemit.rs":"4465cceb68d03ae4d0fdf188c9b86870fb57b3617d67f0bb7d476e5afb581e81","src/isa/arm64/enc_tables.rs":"8c829c544daeed9adc8458891614a0be6f149e775bd22651465f2c165d4a9e56","src/isa/arm64/mod.rs":"f9ca60e7407b69595cb4ef42103ed079e7bcb40546f11d944ddcfc6a04a7fd11","src/isa/arm64/registers.rs":"308cfcfd9ff2191d7656e7350bb36e41803664eb86ae490fb4b4d3549b25b6a2","src/isa/arm64/settings.rs":"0dff47e995e5d9740deb0116b59e91fbcb725a7fa1bdbd802bf83c13381b17f7","src/isa/call_conv.rs":"833ac811ff78ab8d3a5052165e76c51c6da7686020d95462c18074750fb790ed","src/isa/constraints.rs":"bddb5c68e56b122a53d8be215e41d22ccf8c4563630b1486e6eb31c0d3337565","src/isa/enc_tables.rs":"3497f3d701f21d6f952424abf31515fde9e67aea1cde26236c9ee8b033c61ae6","src/isa/encoding.rs":"7ea5b4400530172f96e263561682886ea6c67e706398d44a83933ef7f0ac98a5","src/isa/mod.rs":"40ab1c6d86f903a88bc7ef83b7cdcbee0eedbfdf7112fb7a6749c0c8cc9ee42c","src/isa/registers.rs":"4a91d4888df5eeed1802f34c43a42b82aaf1f9928a58329b0cbc9c3c57c75485","src/isa/riscv/abi.rs":"36557b91ad16a1344c80fbb16a62b46eac88500d76cb9ebcd4eae224dd67b2de","src/isa/riscv/binemit.rs":"0bd76b005b53b71bdb59057a20671fbcd8ab1c37d767bfd4ab0a92d05e192d9a","src/isa/riscv/enc_tables.rs":"ab73c80fef6b1256fbd3c0e1bdd8e43a20f7d132a32236f6bfc028e9003adfe0","src/isa/riscv/mod.rs":"377dfc7dc9940d284b21bf3d2433916dd9c3df79cce172a2a75ef572dcafe98f","src/isa/riscv/registers.rs":"666c2abe1a93db5f1573d1603db6c13c37f3fc877c0c93b64d1b971921bfa950","src/isa/riscv/settings.rs":"d018d23418ff3fb98571bcdeeddde3277bf213b4b8ac1e55b6573e128b3931ce","src/isa/stack.rs":"d023c57feb944c94c4dc8b7138dcbc9d37ff18ca23c23d83e9bab3c88299ffa0","src/isa/x86/abi.rs":"98092944de6b8a8171f1c7d24a63d23ee6dc77a5b4048be113582c5362ac0158","src/isa/x86/binemit.rs":"328f5803717e095abbda0d0a49a79b8f5250a6d16a739a5912ee0a71f276d9de","src/isa/x86/enc_tables.rs":"7f710f042a2be316f6540515b8838df84f26af3a7c5b290a9cafdf36ef92f0a0","src/isa/x86/mod.rs":"15488d60a950aa4cb75afb63d42d4524e1fcea3b77c7c160e2cb862ec2236a92","src/isa/x86/registers.rs":"bed70bbe1f56f3ef03ea7cd1bea68eb911913cb4c8b93167e044dfc639f7f461","src/isa/x86/settings.rs":"cd8f8c5255663f6e247f0634088b16b53d785ee8c62cb5c0926b3d27597c12ff","src/iterators.rs":"f85f52d3fa707a0eb974c92215b3e976923ce8f9481219f7812e0f2869c2bd37","src/legalizer/boundary.rs":"435707e84b5e406843238d27f5cc4886f4ae37e2c4d25a749e189a378b3af632","src/legalizer/call.rs":"6a82a9daeba5f452a7db0c47dcc43829b119b0652961bc2aa336b809c3f34ef1","src/legalizer/globalvalue.rs":"896f59dc5b8c4a5aee45e3f59211066f214d3ab9142865d80ad6610633363ac9","src/legalizer/heap.rs":"6e13c96f364de71c507aec330d991862a8f925947bb1ffbf1c1493c546c5aacd","src/legalizer/libcall.rs":"6e58da5e1c2419192a3393debc6e9714df9728e59e1740ff569e8a9c0daa40d5","src/legalizer/mod.rs":"7df6ed89a80c9dab7f6e3ddbedcb1c4bcc86c3c6e5bdc3e71e51d5322cb1ce52","src/legalizer/split.rs":"13fe4d2cecea166ecdc1ebb11f5254374ee170518f1a61de7ac0a921bc8fb25d","src/legalizer/table.rs":"d6e09f8340ca597fdb13f86021e5c53bd3161dc4258effc56c1f6d9be7b819ec","src/lib.rs":"fc6f6184217eb6e9a9dc92200121a4637347c810cd64a933879fd773cb8f7638","src/licm.rs":"a4b482c995daf0ecf928a525747316760986a42234331554ae68fe9ef8c7145e","src/loop_analysis.rs":"58fc3cc0e700f05e3131ff1b16ff975d4f32a68c790f095d8445bd300356d3c0","src/nan_canonicalization.rs":"9619bb5554791bd1be75ecd98564d6c9f5b65132bc07c5c4d8c210cd79b66f82","src/partition_slice.rs":"bc13504e7658aab565918d965a0b67e941eb572e583870571bc6dbb2b9aad272","src/postopt.rs":"f3af3f27496de14e4e65b7efb0748ca11edff8fedd7b57d680b310de5156aa62","src/predicates.rs":"44bbc09ae0c7d5b54c05eb6061b062e92d1893edc7dda43ae2bccfedc6bb25e3","src/print_errors.rs":"aaa01084e1655f086d958c7e3b54b0ff6345f96aac06e0f65d7330a15ea2fe27","src/ref_slice.rs":"421a61323c11858a596d50220487f399e1bcedeff0e8d1b716dd4b3531eb01a5","src/regalloc/affinity.rs":"66ee6b9789ec207393c318b14177e1439a54f197b13ebefdb0c4ab77acf38c00","src/regalloc/coalescing.rs":"881c8be32eb4d4b34cf208d0dba3e18b8469bc19f19aa7120514c801562392d3","src/regalloc/coloring.rs":"72bef8b5e3425c805d62cf2329acd29510f1be0886ee73308b69938cf2de203f","src/regalloc/context.rs":"b358a31a6e914502d88a4f1fdad20fc66d1b07a7bdb0213001a94c9335875937","src/regalloc/diversion.rs":"d46d733f6d00a8f536d5c7c8b8fc6f348c3d0605dd0ee77e1d8359367ba53347","src/regalloc/live_value_tracker.rs":"28823003dc72e8a4702776a8ab5ffd878712700a272b64376b0de2022e0ee31a","src/regalloc/liveness.rs":"d2d227a0e6074b2d6ca7d564cf60bc1b565697f4502d1943f5ea3993275fbbf9","src/regalloc/liverange.rs":"7a28454e5f70d570db439b966a01ead759b65eb65c5845f9c58bf2f230a5f2ab","src/regalloc/mod.rs":"6254df639f9289fd578e01b7dca99bc9c9e3c6680c6d031405e8df8d0cff31ad","src/regalloc/pressure.rs":"c9d1098266c5b8b89eff84a55be3bc4838b70416c2446edf57f5257a6232d059","src/regalloc/register_set.rs":"bc58f93f22f0adbe43260fe20c6089be1fca64f5bcc4acff85dc0a5ec5b61937","src/regalloc/reload.rs":"4dd64aacc97fda5e950222df37df20bf2281cfbca93d1945f3a4f25e4b867aac","src/regalloc/solver.rs":"e2a6d2782d213676dec9781aeb5a37806e092d6daac3249a6cd4d4c92a9d117f","src/regalloc/spilling.rs":"f21ae054e6546b0cd774a94bb301517ba341b985223b6db36e6c9ba995eecfd8","src/regalloc/virtregs.rs":"e5c8da6860ba9495f9396621530347e1dd6fc5b2fae2eb23c171ea77429356f1","src/result.rs":"c10354d615f93caa446c3c8c49d6ba3af762816af470f9c4accf04315cce9753","src/scoped_hash_map.rs":"5afafb3a4039094c3a2aad1582354220d21f399aa50046e7f4a1259e1976597e","src/settings.rs":"3d60acb7eff7f1681f3b23a1a10f067461c35008b94d3a022eaf9e27d9706f09","src/simple_gvn.rs":"c8feb380d4831badc59aa1e65efeafa6702711585817fe5f6b31de6b265fac24","src/simple_preopt.rs":"d2800d1345dc02e2ff5ef5c8b3d1bf9fcaee8f62e78df9f1681338ffc780d520","src/stack_layout.rs":"c5de271e296fc424f1a30017620bc88500369c8e553fef6e95beccb9c6640e7c","src/timing.rs":"a6808943eec68f5d3ff32132d40c07c142e6aa8073473561573a013978883e4f","src/topo_order.rs":"b01ed68a7300691f41ac434e58a5267b10a8b4a7056d65035e24aa8a6722122a","src/unreachable_code.rs":"40cc71a02887ee4065c76ce96dda0a363a8cc134ec784fe5ed1f276db76596ce","src/value_label.rs":"97ca04bfea0c675117ae74acc55e86a4832359eec7a75166cb39a6b6e0c02fba","src/verifier/cssa.rs":"e3e1d77b763c0ba82d3b59ab5b4667fd3152d5a08be50b58b0c82f86376bb062","src/verifier/flags.rs":"f4ba0e0c13fd643bdbec6466219a25a33993a6e170debb48497a859d9f79d914","src/verifier/liveness.rs":"2631037bafa88659bc47d2174e261f5acb1702ca522722a597fa28e474994d79","src/verifier/locations.rs":"9623bbc2d2f86f36893eebe60330fd51b99c9f9c8e5162c61cc89ab221e75b5a","src/verifier/mod.rs":"0d4a46879a8c4b110b354ba35c667478a785e69b8a1c3b97c7f1aadc79433637","src/write.rs":"c672f1fe00cce47ebbb7e33d60df88f80e36b02a2e3c37b0a0c4027e8dec301f"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-codegen/meta-python/build.py
+++ b/third_party/rust/cranelift-codegen/meta-python/build.py
@@ -1,19 +1,17 @@
 # Second-level build script.
 #
 # This script is run from cranelift-codegen/build.rs to generate Rust files.
 
 from __future__ import absolute_import
 import argparse
 import isa
-import gen_settings
 import gen_build_deps
 import gen_encoding
-import gen_legalizer
 import gen_binemit
 
 try:
     from typing import List, Set  # noqa
     from cdsl.isa import TargetISA  # noqa
     from cdsl.instructions import InstructionGroup  # noqa
 except ImportError:
     pass
@@ -39,17 +37,15 @@ def main():
     parser.add_argument('--out-dir', help='set output directory')
 
     args = parser.parse_args()
     out_dir = args.out_dir
 
     isas = isa.all_isas()
     number_all_instructions(isas)
 
-    gen_settings.generate(isas, out_dir)
     gen_encoding.generate(isas, out_dir)
-    gen_legalizer.generate(isas, out_dir)
     gen_binemit.generate(isas, out_dir)
     gen_build_deps.generate()
 
 
 if __name__ == "__main__":
     main()
--- a/third_party/rust/cranelift-codegen/meta-python/cdsl/ast.py
+++ b/third_party/rust/cranelift-codegen/meta-python/cdsl/ast.py
@@ -157,29 +157,29 @@ class Var(Atom):
     Intermediate values
         Values that are defined in the source pattern, but not in the
         destination pattern. These may have uses outside the source pattern, so
         the defining instruction can't be deleted immediately.
     Temporary values
         Values that are defined only in the destination pattern.
     """
 
-    def __init__(self, name, typevar=None):
-        # type: (str, TypeVar) -> None
+    def __init__(self, name):
+        # type: (str) -> None
         self.name = name
         # The `Def` defining this variable in a source pattern.
         self.src_def = None  # type: Def
         # The `Def` defining this variable in a destination pattern.
         self.dst_def = None  # type: Def
         # TypeVar representing the type of this variable.
-        self.typevar = typevar  # type: TypeVar
+        self.typevar = None  # type: TypeVar
         # The original 'typeof(x)' type variable that was created for this Var.
         # This one doesn't change. `self.typevar` above may be changed to
         # another typevar by type inference.
-        self.original_typevar = self.typevar  # type: TypeVar
+        self.original_typevar = None  # type: TypeVar
 
     def __str__(self):
         # type: () -> str
         return self.name
 
     def __repr__(self):
         # type: () -> str
         s = self.name
--- a/third_party/rust/cranelift-codegen/meta-python/cdsl/settings.py
+++ b/third_party/rust/cranelift-codegen/meta-python/cdsl/settings.py
@@ -224,26 +224,35 @@ class SettingGroup(object):
         :param globs: Pass in `globals()` to run `extract_names` on all
             settings defined in the module.
         """
         assert SettingGroup._current is self, (
                 "Can't close {}, the open setting group is {}"
                 .format(self, SettingGroup._current))
         SettingGroup._current = None
         if globs:
+            # Ensure that named predicates are ordered in a deterministic way
+            # that the Rust crate may simply reproduce, by pushing entries into
+            # a vector that we'll sort by name later.
+            named_predicates = []
+
             for name, obj in globs.items():
                 if isinstance(obj, Setting):
                     assert obj.name is None, obj.name
                     obj.name = name
                 if isinstance(obj, Predicate):
-                    self.named_predicates[name] = obj
+                    named_predicates.append((name, obj))
                 if isinstance(obj, Preset):
                     assert obj.name is None, obj.name
                     obj.name = name
 
+            named_predicates.sort(key=lambda x: x[0])
+            for (name, obj) in named_predicates:
+                self.named_predicates[name] = obj
+
         self.layout()
 
     @staticmethod
     def append(setting):
         # type: (Setting) -> SettingGroup
         g = SettingGroup._current
         assert g, "Open a setting group before defining settings."
         g.settings.append(setting)
deleted file mode 100644
--- a/third_party/rust/cranelift-codegen/meta-python/gen_legalizer.py
+++ /dev/null
@@ -1,468 +0,0 @@
-"""
-Generate legalizer transformations.
-
-The transformations defined in the `cranelift.legalize` module are all of the
-macro-expansion form where the input pattern is a single instruction. We
-generate a Rust function for each `XFormGroup` which takes a `Cursor` pointing
-at the instruction to be legalized. The expanded destination pattern replaces
-the input instruction.
-"""
-from __future__ import absolute_import
-from srcgen import Formatter
-from collections import defaultdict
-from base import instructions
-from cdsl.ast import Var
-from cdsl.ti import ti_rtl, TypeEnv, get_type_env, TypesEqual,\
-    InTypeset, WiderOrEq
-from unique_table import UniqueTable
-from cdsl.typevar import TypeVar
-
-try:
-    from typing import Sequence, List, Dict, Set, DefaultDict # noqa
-    from cdsl.isa import TargetISA  # noqa
-    from cdsl.ast import Def, VarAtomMap  # noqa
-    from cdsl.xform import XForm, XFormGroup  # noqa
-    from cdsl.typevar import TypeSet # noqa
-    from cdsl.ti import TypeConstraint # noqa
-except ImportError:
-    pass
-
-
-# TypeSet indexes are encoded in 8 bits, with `0xff` reserved.
-typeset_limit = 0xff
-
-
-def gen_typesets_table(fmt, type_sets):
-    # type: (Formatter, UniqueTable) -> None
-    """
-    Generate the table of ValueTypeSets described by type_sets.
-    """
-    if len(type_sets.table) == 0:
-        return
-    fmt.comment('Table of value type sets.')
-    assert len(type_sets.table) <= typeset_limit, "Too many type sets"
-    with fmt.indented(
-            'const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = ['
-            .format(len(type_sets.table)), '];'):
-        for ts in type_sets.table:
-            with fmt.indented('ir::instructions::ValueTypeSet {', '},'):
-                ts.emit_fields(fmt)
-
-
-def get_runtime_typechecks(xform):
-    # type: (XForm) -> List[TypeConstraint]
-    """
-    Given a XForm build a list of runtime type checks necessary to determine
-    if it applies. We have 2 types of runtime checks:
-        1) typevar tv belongs to typeset T - needed for free tvs whose
-               typeset is constrained by their use in the dst pattern
-
-        2) tv1 == tv2 where tv1 and tv2 are derived TVs - caused by unification
-                of non-bijective functions
-    """
-    check_l = []  # type: List[TypeConstraint]
-
-    # 1) Perform ti only on the source RTL. Accumulate any free tvs that have a
-    #    different inferred type in src, compared to the type inferred for both
-    #    src and dst.
-    symtab = {}  # type: VarAtomMap
-    src_copy = xform.src.copy(symtab)
-    src_typenv = get_type_env(ti_rtl(src_copy, TypeEnv()))
-
-    for v in xform.ti.vars:
-        if not v.has_free_typevar():
-            continue
-
-        # In rust the local variable containing a free TV associated with var v
-        # has name typeof_v. We rely on the python TVs having the same name.
-        assert "typeof_{}".format(v) == xform.ti[v].name
-
-        if v not in symtab:
-            # We can have singleton vars defined only on dst. Ignore them
-            assert v.get_typevar().singleton_type() is not None
-            continue
-
-        inner_v = symtab[v]
-        assert isinstance(inner_v, Var)
-        src_ts = src_typenv[inner_v].get_typeset()
-        xform_ts = xform.ti[v].get_typeset()
-
-        assert xform_ts.issubset(src_ts)
-        if src_ts != xform_ts:
-            check_l.append(InTypeset(xform.ti[v], xform_ts))
-
-    # 2,3) Add any constraints that appear in xform.ti
-    check_l.extend(xform.ti.constraints)
-
-    return check_l
-
-
-def emit_runtime_typecheck(check, fmt, type_sets):
-    # type: (TypeConstraint, Formatter, UniqueTable) -> None
-    """
-    Emit rust code for the given check.
-
-    The emitted code is a statement redefining the `predicate` variable like
-    this:
-
-        let predicate = predicate && ...
-    """
-    def build_derived_expr(tv):
-        # type: (TypeVar) -> str
-        """
-        Build an expression of type Option<Type> corresponding to a concrete
-        type transformed by the sequence of derivation functions in tv.
-
-        We are using Option<Type>, as some constraints may cause an
-        over/underflow on patterns that do not match them. We want to capture
-        this without panicking at runtime.
-        """
-        if not tv.is_derived:
-            assert tv.name.startswith('typeof_')
-            return "Some({})".format(tv.name)
-
-        base_exp = build_derived_expr(tv.base)
-        if (tv.derived_func == TypeVar.LANEOF):
-            return "{}.map(|t: crate::ir::Type| t.lane_type())"\
-                .format(base_exp)
-        elif (tv.derived_func == TypeVar.ASBOOL):
-            return "{}.map(|t: crate::ir::Type| t.as_bool())".format(base_exp)
-        elif (tv.derived_func == TypeVar.HALFWIDTH):
-            return "{}.and_then(|t: crate::ir::Type| t.half_width())"\
-                .format(base_exp)
-        elif (tv.derived_func == TypeVar.DOUBLEWIDTH):
-            return "{}.and_then(|t: crate::ir::Type| t.double_width())"\
-                .format(base_exp)
-        elif (tv.derived_func == TypeVar.HALFVECTOR):
-            return "{}.and_then(|t: crate::ir::Type| t.half_vector())"\
-                .format(base_exp)
-        elif (tv.derived_func == TypeVar.DOUBLEVECTOR):
-            return "{}.and_then(|t: crate::ir::Type| t.by(2))".format(base_exp)
-        else:
-            assert False, "Unknown derived function {}".format(tv.derived_func)
-
-    if (isinstance(check, InTypeset)):
-        assert not check.tv.is_derived
-        tv = check.tv.name
-        if check.ts not in type_sets.index:
-            type_sets.add(check.ts)
-        ts = type_sets.index[check.ts]
-        fmt.comment("{} must belong to {}".format(tv, check.ts))
-        fmt.format(
-                'let predicate = predicate && TYPE_SETS[{}].contains({});',
-                ts, tv)
-    elif (isinstance(check, TypesEqual)):
-        with fmt.indented(
-            'let predicate = predicate && match ({}, {}) {{'
-            .format(build_derived_expr(check.tv1),
-                    build_derived_expr(check.tv2)), '};'):
-            fmt.line('(Some(a), Some(b)) => a == b,')
-            fmt.comment('On overflow, constraint doesn\'t appply')
-            fmt.line('_ => false,')
-    elif (isinstance(check, WiderOrEq)):
-        with fmt.indented(
-            'let predicate = predicate && match ({}, {}) {{'
-            .format(build_derived_expr(check.tv1),
-                    build_derived_expr(check.tv2)), '};'):
-            fmt.line('(Some(a), Some(b)) => a.wider_or_equal(b),')
-            fmt.comment('On overflow, constraint doesn\'t appply')
-            fmt.line('_ => false,')
-    else:
-        assert False, "Unknown check {}".format(check)
-
-
-def unwrap_inst(iref, node, fmt):
-    # type: (str, Def, Formatter) -> bool
-    """
-    Given a `Def` node, emit code that extracts all the instruction fields from
-    `pos.func.dfg[iref]`.
-
-    Create local variables named after the `Var` instances in `node`.
-
-    Also create a local variable named `predicate` with the value of the
-    evaluated instruction predicate, or `true` if the node has no predicate.
-
-    :param iref: Name of the `Inst` reference to unwrap.
-    :param node: `Def` node providing variable names.
-    :returns: True if the instruction arguments were not detached, expecting a
-              replacement instruction to overwrite the original.
-    """
-    fmt.comment('Unwrap {}'.format(node))
-    expr = node.expr
-    iform = expr.inst.format
-    nvops = iform.num_value_operands
-
-    # The tuple of locals to extract is the `Var` instances in `expr.args`.
-    arg_names = tuple(
-            arg.name if isinstance(arg, Var) else '_' for arg in expr.args)
-    with fmt.indented(
-            'let ({}, predicate) = if let crate::ir::InstructionData::{} {{'
-            .format(', '.join(map(str, arg_names)), iform.name), '};'):
-        # Fields are encoded directly.
-        for f in iform.imm_fields:
-            fmt.line('{},'.format(f.member))
-        if nvops == 1:
-            fmt.line('arg,')
-        elif iform.has_value_list or nvops > 1:
-            fmt.line('ref args,')
-        fmt.line('..')
-        fmt.outdented_line('} = pos.func.dfg[inst] {')
-        fmt.line('let func = &pos.func;')
-        if iform.has_value_list:
-            fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
-        elif nvops == 1:
-            fmt.line('let args = [arg];')
-        # Generate the values for the tuple.
-        with fmt.indented('(', ')'):
-            for opnum, op in enumerate(expr.inst.ins):
-                if op.is_immediate():
-                    n = expr.inst.imm_opnums.index(opnum)
-                    fmt.format('{},', iform.imm_fields[n].member)
-                elif op.is_value():
-                    n = expr.inst.value_opnums.index(opnum)
-                    fmt.format('func.dfg.resolve_aliases(args[{}]),', n)
-            # Evaluate the instruction predicate, if any.
-            instp = expr.inst_predicate_with_ctrl_typevar()
-            fmt.line(instp.rust_predicate(0) if instp else 'true')
-        fmt.outdented_line('} else {')
-        fmt.line('unreachable!("bad instruction format")')
-
-    # Get the types of any variables where it is needed.
-    for opnum in expr.inst.value_opnums:
-        v = expr.args[opnum]
-        if isinstance(v, Var) and v.has_free_typevar():
-            fmt.format('let typeof_{0} = pos.func.dfg.value_type({0});', v)
-
-    # If the node has results, detach the values.
-    # Place the values in locals.
-    replace_inst = False
-    if len(node.defs) > 0:
-        if node.defs == node.defs[0].dst_def.defs:
-            # Special case: The instruction replacing node defines the exact
-            # same values.
-            fmt.comment(
-                    'Results handled by {}.'
-                    .format(node.defs[0].dst_def))
-            replace_inst = True
-        else:
-            # Boring case: Detach the result values, capture them in locals.
-            for d in node.defs:
-                fmt.line('let {};'.format(d))
-            with fmt.indented('{', '}'):
-                fmt.line('let r = pos.func.dfg.inst_results(inst);')
-                for i in range(len(node.defs)):
-                    fmt.line('{} = r[{}];'.format(node.defs[i], i))
-            for d in node.defs:
-                if d.has_free_typevar():
-                    fmt.line(
-                            'let typeof_{0} = pos.func.dfg.value_type({0});'
-                            .format(d))
-
-    return replace_inst
-
-
-def wrap_tup(seq):
-    # type: (Sequence[object]) -> str
-    tup = tuple(map(str, seq))
-    if len(tup) == 1:
-        return tup[0]
-    else:
-        return '({})'.format(', '.join(tup))
-
-
-def is_value_split(node):
-    # type: (Def) -> bool
-    """
-    Determine if `node` represents one of the value splitting instructions:
-    `isplit` or `vsplit. These instructions are lowered specially by the
-    `legalize::split` module.
-    """
-    if len(node.defs) != 2:
-        return False
-    return node.expr.inst in (instructions.isplit, instructions.vsplit)
-
-
-def emit_dst_inst(node, fmt):
-    # type: (Def, Formatter) -> None
-    replaced_inst = None  # type: str
-
-    if is_value_split(node):
-        # Split instructions are not emitted with the builder, but by calling
-        # special functions in the `legalizer::split` module. These functions
-        # will eliminate concat-split patterns.
-        fmt.line('let curpos = pos.position();')
-        fmt.line('let srcloc = pos.srcloc();')
-        fmt.format(
-                'let {} = split::{}(pos.func, cfg, curpos, srcloc, {});',
-                wrap_tup(node.defs),
-                node.expr.inst.snake_name(),
-                node.expr.args[0])
-    else:
-        if len(node.defs) == 0:
-            # This node doesn't define any values, so just insert the new
-            # instruction.
-            builder = 'pos.ins()'
-        else:
-            src_def0 = node.defs[0].src_def
-            if src_def0 and node.defs == src_def0.defs:
-                # The replacement instruction defines the exact same values as
-                # the source pattern. Unwrapping would have left the results
-                # intact.
-                # Replace the whole instruction.
-                builder = 'let {} = pos.func.dfg.replace(inst)'.format(
-                        wrap_tup(node.defs))
-                replaced_inst = 'inst'
-            else:
-                # Insert a new instruction.
-                builder = 'let {} = pos.ins()'.format(wrap_tup(node.defs))
-                # We may want to reuse some of the detached output values.
-                if len(node.defs) == 1 and node.defs[0].is_output():
-                    # Reuse the single source result value.
-                    builder += '.with_result({})'.format(node.defs[0])
-                elif any(d.is_output() for d in node.defs):
-                    # We have some output values to be reused.
-                    array = ', '.join(
-                            ('Some({})'.format(d) if d.is_output()
-                                else 'None')
-                            for d in node.defs)
-                    builder += '.with_results([{}])'.format(array)
-
-        fmt.line('{}.{};'.format(builder, node.expr.rust_builder(node.defs)))
-
-    # If we just replaced an instruction, we need to bump the cursor so
-    # following instructions are inserted *after* the replaced instruction.
-    if replaced_inst:
-        with fmt.indented(
-                'if pos.current_inst() == Some({}) {{'
-                .format(replaced_inst), '}'):
-            fmt.line('pos.next_inst();')
-
-
-def gen_xform(xform, fmt, type_sets):
-    # type: (XForm, Formatter, UniqueTable) -> None
-    """
-    Emit code for `xform`, assuming that the opcode of xform's root instruction
-    has already been matched.
-
-    `inst: Inst` is the variable to be replaced. It is pointed to by `pos:
-    Cursor`.
-    `dfg: DataFlowGraph` is available and mutable.
-    """
-    # Unwrap the source instruction, create local variables for the input
-    # variables.
-    replace_inst = unwrap_inst('inst', xform.src.rtl[0], fmt)
-
-    # Emit any runtime checks.
-    # These will rebind `predicate` emitted by unwrap_inst().
-    for check in get_runtime_typechecks(xform):
-        emit_runtime_typecheck(check, fmt, type_sets)
-
-    # Guard the actual expansion by `predicate`.
-    with fmt.indented('if predicate {', '}'):
-        # If we're going to delete `inst`, we need to detach its results first
-        # so they can be reattached during pattern expansion.
-        if not replace_inst:
-            fmt.line('pos.func.dfg.clear_results(inst);')
-
-        # Emit the destination pattern.
-        for dst in xform.dst.rtl:
-            emit_dst_inst(dst, fmt)
-
-        # Delete the original instruction if we didn't have an opportunity to
-        # replace it.
-        if not replace_inst:
-            fmt.line('let removed = pos.remove_inst();')
-            fmt.line('debug_assert_eq!(removed, inst);')
-        fmt.line('return true;')
-
-
-def gen_xform_group(xgrp, fmt, type_sets):
-    # type: (XFormGroup, Formatter, UniqueTable) -> None
-    fmt.doc_comment("Legalize `inst`.")
-    fmt.line('#[allow(unused_variables,unused_assignments,non_snake_case)]')
-    with fmt.indented('pub fn {}('.format(xgrp.name)):
-        fmt.line('inst: crate::ir::Inst,')
-        fmt.line('func: &mut crate::ir::Function,')
-        fmt.line('cfg: &mut crate::flowgraph::ControlFlowGraph,')
-        fmt.line('isa: &crate::isa::TargetIsa,')
-    with fmt.indented(') -> bool {', '}'):
-        fmt.line('use crate::ir::InstBuilder;')
-        fmt.line('use crate::cursor::{Cursor, FuncCursor};')
-        fmt.line('let mut pos = FuncCursor::new(func).at_inst(inst);')
-        fmt.line('pos.use_srcloc(inst);')
-
-        # Group the xforms by opcode so we can generate a big switch.
-        # Preserve ordering.
-        xforms = defaultdict(list)  # type: DefaultDict[str, List[XForm]]
-        for xform in xgrp.xforms:
-            inst = xform.src.rtl[0].expr.inst
-            xforms[inst.camel_name].append(xform)
-
-        with fmt.indented('{', '}'):
-            with fmt.indented('match pos.func.dfg[inst].opcode() {', '}'):
-                for camel_name in sorted(xforms.keys()):
-                    with fmt.indented(
-                            'ir::Opcode::{} => {{'.format(camel_name), '}'):
-                        for xform in xforms[camel_name]:
-                            gen_xform(xform, fmt, type_sets)
-
-                # Emit the custom transforms. The Rust compiler will complain
-                # about any overlap with the normal xforms.
-                for inst, funcname in xgrp.custom.items():
-                    with fmt.indented(
-                            'ir::Opcode::{} => {{'
-                            .format(inst.camel_name), '}'):
-                        fmt.format('{}(inst, pos.func, cfg, isa);', funcname)
-                        fmt.line('return true;')
-
-                # We'll assume there are uncovered opcodes.
-                fmt.line('_ => {},')
-
-        # If we fall through, nothing was expanded. Call the chain if any.
-        if xgrp.chain:
-            fmt.format('{}(inst, pos.func, cfg, isa)', xgrp.chain.rust_name())
-        else:
-            fmt.line('false')
-
-
-def gen_isa(isa, fmt, shared_groups):
-    # type: (TargetISA, Formatter, Set[XFormGroup]) -> None
-    """
-    Generate legalization functions for `isa` and add any shared `XFormGroup`s
-    encountered to `shared_groups`.
-
-    Generate `TYPE_SETS` and `LEGALIZE_ACTION` tables.
-    """
-    type_sets = UniqueTable()
-    for xgrp in isa.legalize_codes.keys():
-        if xgrp.isa is None:
-            shared_groups.add(xgrp)
-        else:
-            assert xgrp.isa == isa
-            gen_xform_group(xgrp, fmt, type_sets)
-
-    gen_typesets_table(fmt, type_sets)
-
-    with fmt.indented(
-            'pub static LEGALIZE_ACTIONS: [isa::Legalize; {}] = ['
-            .format(len(isa.legalize_codes)), '];'):
-        for xgrp in isa.legalize_codes.keys():
-            fmt.format('{},', xgrp.rust_name())
-
-
-def generate(isas, out_dir):
-    # type: (Sequence[TargetISA], str) -> None
-    shared_groups = set()  # type: Set[XFormGroup]
-
-    for isa in isas:
-        fmt = Formatter()
-        gen_isa(isa, fmt, shared_groups)
-        fmt.update_file('legalize-{}.rs'.format(isa.name), out_dir)
-
-    # Shared xform groups.
-    fmt = Formatter()
-    type_sets = UniqueTable()
-    for xgrp in sorted(shared_groups, key=lambda g: g.name):
-        gen_xform_group(xgrp, fmt, type_sets)
-    gen_typesets_table(fmt, type_sets)
-    fmt.update_file('legalizer.rs', out_dir)
deleted file mode 100644
--- a/third_party/rust/cranelift-codegen/meta-python/gen_settings.py
+++ /dev/null
@@ -1,335 +0,0 @@
-"""
-Generate sources with settings.
-"""
-from __future__ import absolute_import
-import srcgen
-from unique_table import UniqueSeqTable
-import constant_hash
-from cdsl import camel_case
-from cdsl.settings import BoolSetting, NumSetting, EnumSetting
-from base import settings
-
-try:
-    from typing import Sequence, Set, Tuple, List, Union, TYPE_CHECKING  # noqa
-    if TYPE_CHECKING:
-        from cdsl.isa import TargetISA  # noqa
-        from cdsl.settings import Setting, Preset, SettingGroup  # noqa
-        from cdsl.predicates import Predicate, PredContext  # noqa
-except ImportError:
-    pass
-
-
-def gen_to_and_from_str(ty, values, fmt):
-    # type: (str, Tuple[str, ...], srcgen.Formatter) -> None
-    """
-    Emit Display and FromStr implementations for enum settings.
-    """
-    with fmt.indented('impl fmt::Display for {} {{'.format(ty), '}'):
-        with fmt.indented(
-                'fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {',
-                '}'):
-            with fmt.indented('f.write_str(match *self {', '})'):
-                for v in values:
-                    fmt.line('{}::{} => "{}",'
-                             .format(ty, camel_case(v), v))
-
-    with fmt.indented('impl str::FromStr for {} {{'.format(ty), '}'):
-        fmt.line('type Err = ();')
-        with fmt.indented(
-                'fn from_str(s: &str) -> Result<Self, Self::Err> {',
-                '}'):
-            with fmt.indented('match s {', '}'):
-                for v in values:
-                    fmt.line('"{}" => Ok({}::{}),'
-                             .format(v, ty, camel_case(v)))
-                fmt.line('_ => Err(()),')
-
-
-def gen_enum_types(sgrp, fmt):
-    # type: (SettingGroup, srcgen.Formatter) -> None
-    """
-    Emit enum types for any enum settings.
-    """
-    for setting in sgrp.settings:
-        if not isinstance(setting, EnumSetting):
-            continue
-        ty = camel_case(setting.name)
-        fmt.doc_comment('Values for `{}`.'.format(setting))
-        fmt.line('#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]')
-        with fmt.indented('pub enum {} {{'.format(ty), '}'):
-            for v in setting.values:
-                fmt.doc_comment('`{}`.'.format(v))
-                fmt.line(camel_case(v) + ',')
-
-        gen_to_and_from_str(ty, setting.values, fmt)
-
-
-def gen_getter(setting, sgrp, fmt):
-    # type: (Setting, SettingGroup, srcgen.Formatter) -> None
-    """
-    Emit a getter function for `setting`.
-    """
-    fmt.doc_comment(setting.__doc__)
-
-    if isinstance(setting, BoolSetting):
-        proto = 'pub fn {}(&self) -> bool'.format(setting.name)
-        with fmt.indented(proto + ' {', '}'):
-            fmt.line(
-                    'self.numbered_predicate({})'
-                    .format(sgrp.predicate_number[setting]))
-    elif isinstance(setting, NumSetting):
-        proto = 'pub fn {}(&self) -> u8'.format(setting.name)
-        with fmt.indented(proto + ' {', '}'):
-            fmt.line('self.bytes[{}]'.format(setting.byte_offset))
-    elif isinstance(setting, EnumSetting):
-        ty = camel_case(setting.name)
-        proto = 'pub fn {}(&self) -> {}'.format(setting.name, ty)
-        with fmt.indented(proto + ' {', '}'):
-            m = srcgen.Match('self.bytes[{}]'.format(setting.byte_offset))
-            for i, v in enumerate(setting.values):
-                m.arm(str(i), [], '{}::{}'.format(ty, camel_case(v)))
-            m.arm('_', [], 'panic!("Invalid enum value")')
-            fmt.match(m)
-    else:
-        raise AssertionError("Unknown setting kind")
-
-
-def gen_pred_getter(name, pred, sgrp, fmt):
-    # type: (str, Predicate, SettingGroup, srcgen.Formatter) -> None
-    """
-    Emit a getter for a named pre-computed predicate.
-    """
-    fmt.doc_comment('Computed predicate `{}`.'.format(pred.rust_predicate(0)))
-    proto = 'pub fn {}(&self) -> bool'.format(name)
-    with fmt.indented(proto + ' {', '}'):
-        fmt.line(
-                'self.numbered_predicate({})'
-                .format(sgrp.predicate_number[pred]))
-
-
-def gen_getters(sgrp, fmt):
-    # type: (SettingGroup, srcgen.Formatter) -> None
-    """
-    Emit getter functions for all the settings in fmt.
-    """
-    fmt.doc_comment("User-defined settings.")
-    fmt.line('#[allow(dead_code)]')
-    with fmt.indented('impl Flags {', '}'):
-        fmt.doc_comment('Get a view of the boolean predicates.')
-        with fmt.indented(
-                'pub fn predicate_view(&self) -> '
-                'crate::settings::PredicateView {', '}'):
-            fmt.format(
-                    'crate::settings::PredicateView::new(&self.bytes[{}..])',
-                    sgrp.boolean_offset)
-        if sgrp.settings:
-            fmt.doc_comment('Dynamic numbered predicate getter.')
-            with fmt.indented(
-                    'fn numbered_predicate(&self, p: usize) -> bool {', '}'):
-                fmt.line(
-                        'self.bytes[{} + p / 8] & (1 << (p % 8)) != 0'
-                        .format(sgrp.boolean_offset))
-        for setting in sgrp.settings:
-            gen_getter(setting, sgrp, fmt)
-        for name, pred in sgrp.named_predicates.items():
-            gen_pred_getter(name, pred, sgrp, fmt)
-
-
-def gen_descriptors(sgrp, fmt):
-    # type: (SettingGroup, srcgen.Formatter) -> None
-    """
-    Generate the DESCRIPTORS, ENUMERATORS, and PRESETS tables.
-    """
-
-    enums = UniqueSeqTable()
-
-    with fmt.indented(
-            'static DESCRIPTORS: [detail::Descriptor; {}] = ['
-            .format(len(sgrp.settings) + len(sgrp.presets)),
-            '];'):
-        for idx, setting in enumerate(sgrp.settings):
-            setting.descriptor_index = idx
-            with fmt.indented('detail::Descriptor {', '},'):
-                fmt.line('name: "{}",'.format(setting.name))
-                fmt.line('offset: {},'.format(setting.byte_offset))
-                if isinstance(setting, BoolSetting):
-                    fmt.line(
-                            'detail: detail::Detail::Bool {{ bit: {} }},'
-                            .format(setting.bit_offset))
-                elif isinstance(setting, NumSetting):
-                    fmt.line('detail: detail::Detail::Num,')
-                elif isinstance(setting, EnumSetting):
-                    offs = enums.add(setting.values)
-                    fmt.line(
-                            'detail: detail::Detail::Enum ' +
-                            '{{ last: {}, enumerators: {} }},'
-                            .format(len(setting.values)-1, offs))
-                else:
-                    raise AssertionError("Unknown setting kind")
-
-        for idx, preset in enumerate(sgrp.presets):
-            preset.descriptor_index = len(sgrp.settings) + idx
-            with fmt.indented('detail::Descriptor {', '},'):
-                fmt.line('name: "{}",'.format(preset.name))
-                fmt.line('offset: {},'.format(idx * sgrp.settings_size))
-                fmt.line('detail: detail::Detail::Preset,')
-
-    with fmt.indented(
-            'static ENUMERATORS: [&str; {}] = ['
-            .format(len(enums.table)),
-            '];'):
-        for txt in enums.table:
-            fmt.line('"{}",'.format(txt))
-
-    def hash_setting(s):
-        # type: (Union[Setting, Preset]) -> int
-        return constant_hash.simple_hash(s.name)
-
-    hash_elems = []  # type: List[Union[Setting, Preset]]
-    hash_elems.extend(sgrp.settings)
-    hash_elems.extend(sgrp.presets)
-    hash_table = constant_hash.compute_quadratic(hash_elems, hash_setting)
-    with fmt.indented(
-            'static HASH_TABLE: [u16; {}] = ['
-            .format(len(hash_table)),
-            '];'):
-        for h in hash_table:
-            if h is None:
-                fmt.line('0xffff,')
-            else:
-                fmt.line('{},'.format(h.descriptor_index))
-
-    with fmt.indented(
-            'static PRESETS: [(u8, u8); {}] = ['
-            .format(len(sgrp.presets) * sgrp.settings_size),
-            '];'):
-        for preset in sgrp.presets:
-            fmt.comment(preset.name)
-            for mask, value in preset.layout():
-                fmt.format('(0b{:08b}, 0b{:08b}),', mask, value)
-
-
-def gen_template(sgrp, fmt):
-    # type: (SettingGroup, srcgen.Formatter) -> None
-    """
-    Emit a Template constant.
-    """
-    v = [0] * sgrp.settings_size
-    for setting in sgrp.settings:
-        v[setting.byte_offset] |= setting.default_byte()
-
-    with fmt.indented(
-            'static TEMPLATE: detail::Template = detail::Template {', '};'):
-        fmt.line('name: "{}",'.format(sgrp.name))
-        fmt.line('descriptors: &DESCRIPTORS,')
-        fmt.line('enumerators: &ENUMERATORS,')
-        fmt.line('hash_table: &HASH_TABLE,')
-        vs = ', '.join('{:#04x}'.format(x) for x in v)
-        fmt.line('defaults: &[{}],'.format(vs))
-        fmt.line('presets: &PRESETS,')
-
-    fmt.doc_comment(
-            'Create a `settings::Builder` for the {} settings group.'
-            .format(sgrp.name))
-    with fmt.indented('pub fn builder() -> Builder {', '}'):
-        fmt.line('Builder::new(&TEMPLATE)')
-
-
-def gen_display(sgrp, fmt):
-    # type: (SettingGroup, srcgen.Formatter) -> None
-    """
-    Generate the Display impl for Flags.
-    """
-    with fmt.indented('impl fmt::Display for Flags {', '}'):
-        with fmt.indented(
-                'fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {',
-                '}'):
-            fmt.line('writeln!(f, "[{}]")?;'.format(sgrp.name))
-            with fmt.indented('for d in &DESCRIPTORS {', '}'):
-                with fmt.indented('if !d.detail.is_preset() {', '}'):
-                    fmt.line('write!(f, "{} = ", d.name)?;')
-                    fmt.line(
-                            'TEMPLATE.format_toml_value(d.detail, ' +
-                            'self.bytes[d.offset as usize], f)?;')
-                    fmt.line('writeln!(f)?;')
-            fmt.line('Ok(())')
-
-
-def gen_constructor(sgrp, fmt):
-    # type: (SettingGroup, srcgen.Formatter) -> None
-    """
-    Generate a Flags constructor.
-    """
-
-    with fmt.indented('impl Flags {', '}'):
-        args = 'builder: Builder'
-        if sgrp.parent:
-            p = sgrp.parent
-            args = '{}: &{}::Flags, {}'.format(p.name, p.qual_mod, args)
-        fmt.doc_comment('Create flags {} settings group.'.format(sgrp.name))
-        fmt.line('#[allow(unused_variables)]')
-        with fmt.indented(
-                'pub fn new({}) -> Self {{'.format(args), '}'):
-            fmt.line('let bvec = builder.state_for("{}");'.format(sgrp.name))
-            fmt.line(
-                'let mut {} = Self {{ bytes: [0; {}] }};'
-                .format(sgrp.name, sgrp.byte_size()))
-            fmt.line(
-                'debug_assert_eq!(bvec.len(), {});'
-                .format(sgrp.settings_size))
-            fmt.line(
-                '{}.bytes[0..{}].copy_from_slice(&bvec);'
-                .format(sgrp.name, sgrp.settings_size))
-
-            # Now compute the predicates.
-            for pred, number in sgrp.predicate_number.items():
-                # Don't compute our own settings.
-                if number < sgrp.boolean_settings:
-                    continue
-                fmt.comment('Precompute #{}.'.format(number))
-                with fmt.indented(
-                        'if {} {{'.format(pred.rust_predicate(0)),
-                        '}'):
-                    fmt.line(
-                            '{}.bytes[{}] |= 1 << {};'
-                            .format(
-                                sgrp.name,
-                                sgrp.boolean_offset + number // 8,
-                                number % 8))
-
-            fmt.line(sgrp.name)
-
-
-def gen_group(sgrp, fmt):
-    # type: (SettingGroup, srcgen.Formatter) -> None
-    """
-    Generate a Flags struct representing `sgrp`.
-    """
-    fmt.line('#[derive(Clone)]')
-    fmt.doc_comment('Flags group `{}`.'.format(sgrp.name))
-    with fmt.indented('pub struct Flags {', '}'):
-        fmt.line('bytes: [u8; {}],'.format(sgrp.byte_size()))
-
-    gen_constructor(sgrp, fmt)
-    gen_enum_types(sgrp, fmt)
-    gen_getters(sgrp, fmt)
-    gen_descriptors(sgrp, fmt)
-    gen_template(sgrp, fmt)
-    gen_display(sgrp, fmt)
-
-
-def generate(isas, out_dir):
-    # type: (Sequence[TargetISA], str) -> None
-    # Generate shared settings.
-    fmt = srcgen.Formatter()
-    settings.group.qual_mod = 'settings'
-    gen_group(settings.group, fmt)
-    fmt.update_file('settings.rs', out_dir)
-
-    # Generate ISA-specific settings.
-    for isa in isas:
-        isa.settings.qual_mod = 'isa::{}::settings'.format(
-                isa.settings.name)
-        fmt = srcgen.Formatter()
-        gen_group(isa.settings, fmt)
-        fmt.update_file('settings-{}.rs'.format(isa.name), out_dir)
--- a/third_party/rust/cranelift-codegen/meta-python/isa/x86/encodings.py
+++ b/third_party/rust/cranelift-codegen/meta-python/isa/x86/encodings.py
@@ -1,27 +1,27 @@
 """
 x86 Encodings.
 """
 from __future__ import absolute_import
 from cdsl.predicates import IsZero32BitFloat, IsZero64BitFloat
-from cdsl.predicates import IsUnsignedInt, Not, And
+from cdsl.predicates import IsUnsignedInt
 from base.predicates import IsColocatedFunc, IsColocatedData, LengthEquals
 from base import instructions as base
 from base import types
 from base.formats import UnaryIeee32, UnaryIeee64, UnaryImm
 from base.formats import FuncAddr, Call, LoadComplex, StoreComplex
 from .defs import X86_64, X86_32
 from . import recipes as r
 from . import settings as cfg
 from . import instructions as x86
 from .legalize import x86_expand
 from base.legalize import narrow, widen, expand_flags
-from base.settings import allones_funcaddrs, is_pic
-from .settings import use_sse41
+from .settings import use_sse41, not_all_ones_funcaddrs_and_not_is_pic, \
+    all_ones_funcaddrs_and_not_is_pic, is_pic, not_is_pic
 
 try:
     from typing import TYPE_CHECKING, Any  # noqa
     if TYPE_CHECKING:
         from cdsl.instructions import MaybeBoundInst  # noqa
         from cdsl.predicates import FieldPredicate # noqa
 except ImportError:
     pass
@@ -402,44 +402,44 @@ enc_both(base.spill.f64, r.fspillSib32, 
 enc_both(base.regspill.f64, r.fregspill32, 0xf2, 0x0f, 0x11)
 
 #
 # Function addresses.
 #
 
 # Non-PIC, all-ones funcaddresses.
 X86_32.enc(base.func_addr.i32, *r.fnaddr4(0xb8),
-           isap=And(Not(allones_funcaddrs), Not(is_pic)))
+           isap=not_all_ones_funcaddrs_and_not_is_pic)
 X86_64.enc(base.func_addr.i64, *r.fnaddr8.rex(0xb8, w=1),
-           isap=And(Not(allones_funcaddrs), Not(is_pic)))
+           isap=not_all_ones_funcaddrs_and_not_is_pic)
 
 # Non-PIC, all-zeros funcaddresses.
 X86_32.enc(base.func_addr.i32, *r.allones_fnaddr4(0xb8),
-           isap=And(allones_funcaddrs, Not(is_pic)))
+           isap=all_ones_funcaddrs_and_not_is_pic)
 X86_64.enc(base.func_addr.i64, *r.allones_fnaddr8.rex(0xb8, w=1),
-           isap=And(allones_funcaddrs, Not(is_pic)))
+           isap=all_ones_funcaddrs_and_not_is_pic)
 
 # 64-bit, colocated, both PIC and non-PIC. Use the lea instruction's
 # pc-relative field.
 X86_64.enc(base.func_addr.i64, *r.pcrel_fnaddr8.rex(0x8d, w=1),
            instp=IsColocatedFunc(FuncAddr.func_ref))
 
 # 64-bit, non-colocated, PIC.
 X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
            isap=is_pic)
 
 #
 # Global addresses.
 #
 
 # Non-PIC
 X86_32.enc(base.symbol_value.i32, *r.gvaddr4(0xb8),
-           isap=Not(is_pic))
+           isap=not_is_pic)
 X86_64.enc(base.symbol_value.i64, *r.gvaddr8.rex(0xb8, w=1),
-           isap=Not(is_pic))
+           isap=not_is_pic)
 
 # PIC, colocated
 X86_64.enc(base.symbol_value.i64, *r.pcrel_gvaddr8.rex(0x8d, w=1),
            isap=is_pic,
            instp=IsColocatedData())
 
 # PIC, non-colocated
 X86_64.enc(base.symbol_value.i64, *r.got_gvaddr8.rex(0x8b, w=1),
--- a/third_party/rust/cranelift-codegen/meta-python/isa/x86/settings.py
+++ b/third_party/rust/cranelift-codegen/meta-python/isa/x86/settings.py
@@ -1,14 +1,14 @@
 """
 x86 settings.
 """
 from __future__ import absolute_import
 from cdsl.settings import SettingGroup, BoolSetting, Preset
-from cdsl.predicates import And
+from cdsl.predicates import And, Not
 import base.settings as shared
 from .defs import ISA
 
 ISA.settings = SettingGroup('x86', parent=shared.group)
 
 # The has_* settings here correspond to CPUID bits.
 
 # CPUID.01H:ECX
@@ -30,16 +30,23 @@ has_lzcnt = BoolSetting("LZCNT: CPUID.EA
 # The use_* settings here are used to determine if a feature can be used.
 
 use_sse41 = And(has_sse41)
 use_sse42 = And(has_sse42, use_sse41)
 use_popcnt = And(has_popcnt, has_sse42)
 use_bmi1 = And(has_bmi1)
 use_lzcnt = And(has_lzcnt)
 
+is_pic = And(shared.is_pic)
+not_is_pic = Not(shared.is_pic)
+all_ones_funcaddrs_and_not_is_pic = And(shared.allones_funcaddrs,
+                                        Not(shared.is_pic))
+not_all_ones_funcaddrs_and_not_is_pic = And(Not(shared.allones_funcaddrs),
+                                            Not(shared.is_pic))
+
 # Presets corresponding to x86 CPUs.
 
 baseline = Preset()
 
 nehalem = Preset(
         has_sse3, has_ssse3, has_sse41, has_sse42, has_popcnt)
 haswell = Preset(nehalem, has_bmi1, has_bmi2, has_lzcnt)
 broadwell = Preset(haswell)
deleted file mode 100644
--- a/third_party/rust/cranelift-codegen/meta-python/test_gen_legalizer.py
+++ /dev/null
@@ -1,196 +0,0 @@
-import doctest
-import gen_legalizer
-from unittest import TestCase
-from srcgen import Formatter
-from gen_legalizer import get_runtime_typechecks, emit_runtime_typecheck
-from base.instructions import vselect, vsplit, isplit, iconcat, vconcat, \
-    iconst, b1, icmp, copy, sextend, uextend, ireduce, fdemote, fpromote # noqa
-from base.legalize import narrow, expand # noqa
-from base.immediates import intcc # noqa
-from cdsl.typevar import TypeVar, TypeSet
-from cdsl.ast import Var, Def # noqa
-from cdsl.xform import Rtl, XForm # noqa
-from cdsl.ti import ti_rtl, subst, TypeEnv, get_type_env # noqa
-from unique_table import UniqueTable
-from functools import reduce
-
-try:
-    from typing import Callable, TYPE_CHECKING, Iterable, Any # noqa
-    if TYPE_CHECKING:
-        CheckProducer = Callable[[UniqueTable], str]
-except ImportError:
-    TYPE_CHECKING = False
-
-
-def load_tests(loader, tests, ignore):
-    # type: (Any, Any, Any) -> Any
-    tests.addTests(doctest.DocTestSuite(gen_legalizer))
-    return tests
-
-
-def format_check(typesets, s, *args):
-    # type: (...) -> str
-    def transform(x):
-        # type: (Any) -> str
-        if isinstance(x, TypeSet):
-            return str(typesets.index[x])
-        elif isinstance(x, TypeVar):
-            assert not x.is_derived
-            return x.name
-        else:
-            return str(x)
-
-    dummy_s = s  # type: str
-    args = tuple(map(lambda x:  transform(x), args))
-    return dummy_s.format(*args)
-
-
-def typeset_check(v, ts):
-    # type: (Var, TypeSet) -> CheckProducer
-    return lambda typesets: format_check(
-        typesets,
-        'let predicate = predicate && TYPE_SETS[{}].contains(typeof_{});\n',
-        ts, v)
-
-
-def equiv_check(tv1, tv2):
-    # type: (str, str) -> CheckProducer
-    return lambda typesets: format_check(
-        typesets,
-        'let predicate = predicate && match ({}, {}) {{\n'
-        '    (Some(a), Some(b)) => a == b,\n'
-        '    _ => false,\n'
-        '}};\n', tv1, tv2)
-
-
-def wider_check(tv1, tv2):
-    # type: (str, str) -> CheckProducer
-    return lambda typesets: format_check(
-        typesets,
-        'let predicate = predicate && match ({}, {}) {{\n'
-        '    (Some(a), Some(b)) => a.wider_or_equal(b),\n'
-        '    _ => false,\n'
-        '}};\n', tv1, tv2)
-
-
-def sequence(*args):
-    # type: (...) -> CheckProducer
-    dummy = args  # type: Iterable[CheckProducer]
-
-    def sequenceF(typesets):
-        # type: (UniqueTable) -> str
-        def strconcat(acc, el):
-            # type: (str, CheckProducer) -> str
-            return acc + el(typesets)
-
-        return reduce(strconcat, dummy, "")
-    return sequenceF
-
-
-class TestRuntimeChecks(TestCase):
-
-    def setUp(self):
-        # type: () -> None
-        self.v0 = Var("v0")
-        self.v1 = Var("v1")
-        self.v2 = Var("v2")
-        self.v3 = Var("v3")
-        self.v4 = Var("v4")
-        self.v5 = Var("v5")
-        self.v6 = Var("v6")
-        self.v7 = Var("v7")
-        self.v8 = Var("v8")
-        self.v9 = Var("v9")
-        self.imm0 = Var("imm0")
-        self.IxN_nonscalar = TypeVar("IxN_nonscalar", "", ints=True,
-                                     scalars=False, simd=True)
-        self.TxN = TypeVar("TxN", "", ints=True, bools=True, floats=True,
-                           scalars=False, simd=True)
-        self.b1 = TypeVar.singleton(b1)
-
-    def check_yo_check(self, xform, expected_f):
-        # type: (XForm, CheckProducer) -> None
-        fmt = Formatter()
-        type_sets = UniqueTable()
-        for check in get_runtime_typechecks(xform):
-            emit_runtime_typecheck(check, fmt, type_sets)
-
-        # Remove comments
-        got = "".join([l for l in fmt.lines if not l.strip().startswith("//")])
-        expected = expected_f(type_sets)
-        self.assertEqual(got, expected)
-
-    def test_width_check(self):
-        # type: () -> None
-        x = XForm(Rtl(self.v0 << copy(self.v1)),
-                  Rtl((self.v2, self.v3) << isplit(self.v1),
-                      self.v0 << iconcat(self.v2, self.v3)))
-
-        WideInt = TypeSet(lanes=(1, 256), ints=(16, 64))
-        self.check_yo_check(x, typeset_check(self.v1, WideInt))
-
-    def test_lanes_check(self):
-        # type: () -> None
-        x = XForm(Rtl(self.v0 << copy(self.v1)),
-                  Rtl((self.v2, self.v3) << vsplit(self.v1),
-                      self.v0 << vconcat(self.v2, self.v3)))
-
-        WideVec = TypeSet(lanes=(2, 256), ints=(8, 64), floats=(32, 64),
-                          bools=(1, 64))
-        self.check_yo_check(x, typeset_check(self.v1, WideVec))
-
-    def test_vselect_imm(self):
-        # type: () -> None
-        ts = TypeSet(lanes=(2, 256), ints=True, floats=True, bools=(8, 64))
-        r = Rtl(
-                self.v0 << iconst(self.imm0),
-                self.v1 << icmp(intcc.eq, self.v2, self.v0),
-                self.v5 << vselect(self.v1, self.v3, self.v4),
-        )
-        x = XForm(r, r)
-        tv2_exp = 'Some({}).map(|t: crate::ir::Type| t.as_bool())'\
-            .format(self.v2.get_typevar().name)
-        tv3_exp = 'Some({}).map(|t: crate::ir::Type| t.as_bool())'\
-            .format(self.v3.get_typevar().name)
-
-        self.check_yo_check(
-            x, sequence(typeset_check(self.v3, ts),
-                        equiv_check(tv2_exp, tv3_exp)))
-
-    def test_reduce_extend(self):
-        # type: () -> None
-        r = Rtl(
-            self.v1 << uextend(self.v0),
-            self.v2 << ireduce(self.v1),
-            self.v3 << sextend(self.v2),
-        )
-        x = XForm(r, r)
-
-        tv0_exp = 'Some({})'.format(self.v0.get_typevar().name)
-        tv1_exp = 'Some({})'.format(self.v1.get_typevar().name)
-        tv2_exp = 'Some({})'.format(self.v2.get_typevar().name)
-        tv3_exp = 'Some({})'.format(self.v3.get_typevar().name)
-
-        self.check_yo_check(
-            x, sequence(wider_check(tv1_exp, tv0_exp),
-                        wider_check(tv1_exp, tv2_exp),
-                        wider_check(tv3_exp, tv2_exp)))
-
-    def test_demote_promote(self):
-        # type: () -> None
-        r = Rtl(
-            self.v1 << fpromote(self.v0),
-            self.v2 << fdemote(self.v1),
-            self.v3 << fpromote(self.v2),
-        )
-        x = XForm(r, r)
-
-        tv0_exp = 'Some({})'.format(self.v0.get_typevar().name)
-        tv1_exp = 'Some({})'.format(self.v1.get_typevar().name)
-        tv2_exp = 'Some({})'.format(self.v2.get_typevar().name)
-        tv3_exp = 'Some({})'.format(self.v3.get_typevar().name)
-
-        self.check_yo_check(
-            x, sequence(wider_check(tv1_exp, tv0_exp),
-                        wider_check(tv1_exp, tv2_exp),
-                        wider_check(tv3_exp, tv2_exp)))
--- a/third_party/rust/cranelift-codegen/src/context.rs
+++ b/third_party/rust/cranelift-codegen/src/context.rs
@@ -24,16 +24,17 @@ use crate::nan_canonicalization::do_nan_
 use crate::postopt::do_postopt;
 use crate::regalloc;
 use crate::result::CodegenResult;
 use crate::settings::{FlagsOrIsa, OptLevel};
 use crate::simple_gvn::do_simple_gvn;
 use crate::simple_preopt::do_preopt;
 use crate::timing;
 use crate::unreachable_code::eliminate_unreachable_code;
+use crate::value_label::{build_value_labels_ranges, ComparableSourceLoc, ValueLabelsRanges};
 use crate::verifier::{verify_context, verify_locations, VerifierErrors, VerifierResult};
 use std::vec::Vec;
 
 /// Persistent data structures and compilation pipeline.
 pub struct Context {
     /// The function we're compiling.
     pub func: Function,
 
@@ -326,9 +327,18 @@ impl Context {
 
     /// Run the branch relaxation pass and return the final code size.
     pub fn relax_branches(&mut self, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
         let code_size = relax_branches(&mut self.func, isa)?;
         self.verify_if(isa)?;
         self.verify_locations_if(isa)?;
         Ok(code_size)
     }
+
+    /// Builds ranges and location for specified value labels.
+    pub fn build_value_labels_ranges(&self, isa: &TargetIsa) -> CodegenResult<ValueLabelsRanges> {
+        Ok(build_value_labels_ranges::<ComparableSourceLoc>(
+            &self.func,
+            &self.regalloc,
+            isa,
+        ))
+    }
 }
--- a/third_party/rust/cranelift-codegen/src/divconst_magic_numbers.rs
+++ b/third_party/rust/cranelift-codegen/src/divconst_magic_numbers.rs
@@ -50,17 +50,17 @@ pub fn magic_u32(d: u32) -> MU32 {
     let mut q2: u32 = 0x7FFFFFFFu32 / d;
     let mut r2: u32 = 0x7FFFFFFFu32 - q2 * d;
     loop {
         p = p + 1;
         if r1 >= nc - r1 {
             q1 = u32::wrapping_add(u32::wrapping_mul(2, q1), 1);
             r1 = u32::wrapping_sub(u32::wrapping_mul(2, r1), nc);
         } else {
-            q1 = 2 * q1;
+            q1 = u32::wrapping_mul(2, q1);
             r1 = 2 * r1;
         }
         if r2 + 1 >= d - r2 {
             if q2 >= 0x7FFFFFFFu32 {
                 do_add = true;
             }
             q2 = 2 * q2 + 1;
             r2 = u32::wrapping_sub(u32::wrapping_add(u32::wrapping_mul(2, r2), 1), d);
@@ -96,17 +96,17 @@ pub fn magic_u64(d: u64) -> MU64 {
     let mut q2: u64 = 0x7FFFFFFFFFFFFFFFu64 / d;
     let mut r2: u64 = 0x7FFFFFFFFFFFFFFFu64 - q2 * d;
     loop {
         p = p + 1;
         if r1 >= nc - r1 {
             q1 = u64::wrapping_add(u64::wrapping_mul(2, q1), 1);
             r1 = u64::wrapping_sub(u64::wrapping_mul(2, r1), nc);
         } else {
-            q1 = 2 * q1;
+            q1 = u64::wrapping_mul(2, q1);
             r1 = 2 * r1;
         }
         if r2 + 1 >= d - r2 {
             if q2 >= 0x7FFFFFFFFFFFFFFFu64 {
                 do_add = true;
             }
             q2 = 2 * q2 + 1;
             r2 = u64::wrapping_sub(u64::wrapping_add(u64::wrapping_mul(2, r2), 1), d);
@@ -517,78 +517,134 @@ mod tests {
             magic_s64(0x7fffffffffffffffi64),
             make_ms64(0x4000000000000001u64 as i64, 61)
         );
     }
 
     #[test]
     fn test_magic_generators_dont_panic() {
         // The point of this is to check that the magic number generators
-        // don't panic with integer wraparounds, especially at boundary
-        // cases for their arguments. The actual results are thrown away.
+        // don't panic with integer wraparounds, especially at boundary cases
+        // for their arguments. The actual results are thrown away, although
+        // we force `total` to be used, so that rustc can't optimise the
+        // entire computation away.
+
+        // Testing UP magic_u32
         let mut total: u64 = 0;
-        // Testing UP magic_u32
         for x in 2..(200 * 1000u32) {
             let m = magic_u32(x);
             total = total ^ (m.mul_by as u64);
             total = total + (m.shift_by as u64);
-            total = total - (if m.do_add { 123 } else { 456 });
+            total = total + (if m.do_add { 123 } else { 456 });
         }
-        assert_eq!(total, 1747815691);
+        assert_eq!(total, 2481999609);
+
+        total = 0;
+        // Testing MIDPOINT magic_u32
+        for x in 0x8000_0000u32 - 10 * 1000u32..0x8000_0000u32 + 10 * 1000u32 {
+            let m = magic_u32(x);
+            total = total ^ (m.mul_by as u64);
+            total = total + (m.shift_by as u64);
+            total = total + (if m.do_add { 123 } else { 456 });
+        }
+        assert_eq!(total, 2399809723);
+
+        total = 0;
         // Testing DOWN magic_u32
         for x in 0..(200 * 1000u32) {
             let m = magic_u32(0xFFFF_FFFFu32 - x);
             total = total ^ (m.mul_by as u64);
             total = total + (m.shift_by as u64);
-            total = total - (if m.do_add { 123 } else { 456 });
+            total = total + (if m.do_add { 123 } else { 456 });
         }
-        assert_eq!(total, 2210292772);
+        assert_eq!(total, 271138267);
 
         // Testing UP magic_u64
+        total = 0;
         for x in 2..(200 * 1000u64) {
             let m = magic_u64(x);
             total = total ^ m.mul_by;
             total = total + (m.shift_by as u64);
-            total = total - (if m.do_add { 123 } else { 456 });
+            total = total + (if m.do_add { 123 } else { 456 });
         }
-        assert_eq!(total, 7430004084791260605);
+        assert_eq!(total, 7430004086976261161);
+
+        total = 0;
+        // Testing MIDPOINT magic_u64
+        for x in 0x8000_0000_0000_0000u64 - 10 * 1000u64..0x8000_0000_0000_0000u64 + 10 * 1000u64 {
+            let m = magic_u64(x);
+            total = total ^ m.mul_by;
+            total = total + (m.shift_by as u64);
+            total = total + (if m.do_add { 123 } else { 456 });
+        }
+        assert_eq!(total, 10312117246769520603);
+
         // Testing DOWN magic_u64
+        total = 0;
         for x in 0..(200 * 1000u64) {
             let m = magic_u64(0xFFFF_FFFF_FFFF_FFFFu64 - x);
             total = total ^ m.mul_by;
             total = total + (m.shift_by as u64);
-            total = total - (if m.do_add { 123 } else { 456 });
+            total = total + (if m.do_add { 123 } else { 456 });
         }
-        assert_eq!(total, 7547519887519825919);
+        assert_eq!(total, 1126603594357269734);
 
         // Testing UP magic_s32
+        total = 0;
         for x in 0..(200 * 1000i32) {
             let m = magic_s32(-0x8000_0000i32 + x);
             total = total ^ (m.mul_by as u64);
             total = total + (m.shift_by as u64);
         }
-        assert_eq!(total, 10899224186731671235);
+        assert_eq!(total, 18446744069953376812);
+
+        total = 0;
+        // Testing MIDPOINT magic_s32
+        for x in 0..(200 * 1000i32) {
+            let x2 = -100 * 1000i32 + x;
+            if x2 != -1 && x2 != 0 && x2 != 1 {
+                let m = magic_s32(x2);
+                total = total ^ (m.mul_by as u64);
+                total = total + (m.shift_by as u64);
+            }
+        }
+        assert_eq!(total, 351839350);
+
         // Testing DOWN magic_s32
+        total = 0;
         for x in 0..(200 * 1000i32) {
             let m = magic_s32(0x7FFF_FFFFi32 - x);
             total = total ^ (m.mul_by as u64);
             total = total + (m.shift_by as u64);
         }
-        assert_eq!(total, 7547519887517897369);
+        assert_eq!(total, 18446744072916880714);
 
         // Testing UP magic_s64
+        total = 0;
         for x in 0..(200 * 1000i64) {
             let m = magic_s64(-0x8000_0000_0000_0000i64 + x);
             total = total ^ (m.mul_by as u64);
             total = total + (m.shift_by as u64);
         }
-        assert_eq!(total, 8029756891368555163);
+        assert_eq!(total, 17929885647724831014);
+
+        total = 0;
+        // Testing MIDPOINT magic_s64
+        for x in 0..(200 * 1000i64) {
+            let x2 = -100 * 1000i64 + x;
+            if x2 != -1 && x2 != 0 && x2 != 1 {
+                let m = magic_s64(x2);
+                total = total ^ (m.mul_by as u64);
+                total = total + (m.shift_by as u64);
+            }
+        }
+        assert_eq!(total, 18106042338125661964);
+
         // Testing DOWN magic_s64
+        total = 0;
         for x in 0..(200 * 1000i64) {
             let m = magic_s64(0x7FFF_FFFF_FFFF_FFFFi64 - x);
             total = total ^ (m.mul_by as u64);
             total = total + (m.shift_by as u64);
         }
-        // Force `total` -- and hence, the entire computation -- to
-        // be used, so that rustc can't optimise it out.
-        assert_eq!(total, 7547519887532559585u64);
+        assert_eq!(total, 563301797155560970);
     }
 }
--- a/third_party/rust/cranelift-codegen/src/ir/dfg.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/dfg.rs
@@ -1,25 +1,29 @@
 //! Data flow graph tracking Instructions, Values, and EBBs.
 
 use crate::entity::{self, PrimaryMap, SecondaryMap};
 use crate::ir;
 use crate::ir::builder::ReplaceBuilder;
 use crate::ir::extfunc::ExtFuncData;
 use crate::ir::instructions::{BranchInfo, CallInfo, InstructionData};
 use crate::ir::types;
-use crate::ir::{Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueList, ValueListPool};
+use crate::ir::{
+    Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueLabelAssignments, ValueList,
+    ValueListPool,
+};
 use crate::isa::TargetIsa;
 use crate::packed_option::ReservedValue;
 use crate::write::write_operands;
 use core::fmt;
 use core::iter;
 use core::mem;
 use core::ops::{Index, IndexMut};
 use core::u16;
+use std::collections::HashMap;
 
 /// A data flow graph defines all instructions and extended basic blocks in a function as well as
 /// the data flow dependencies between them. The DFG also tracks values which can be either
 /// instruction results or EBB parameters.
 ///
 /// The layout of EBBs in the function and of instructions in each EBB is recorded by the
 /// `FunctionLayout` data structure which form the other half of the function representation.
 ///
@@ -55,41 +59,46 @@ pub struct DataFlowGraph {
     values: PrimaryMap<Value, ValueData>,
 
     /// Function signature table. These signatures are referenced by indirect call instructions as
     /// well as the external function references.
     pub signatures: PrimaryMap<SigRef, Signature>,
 
     /// External function references. These are functions that can be called directly.
     pub ext_funcs: PrimaryMap<FuncRef, ExtFuncData>,
+
+    /// Saves Value labels.
+    pub values_labels: Option<HashMap<Value, ValueLabelAssignments>>,
 }
 
 impl DataFlowGraph {
     /// Create a new empty `DataFlowGraph`.
     pub fn new() -> Self {
         Self {
             insts: PrimaryMap::new(),
             results: SecondaryMap::new(),
             ebbs: PrimaryMap::new(),
             value_lists: ValueListPool::new(),
             values: PrimaryMap::new(),
             signatures: PrimaryMap::new(),
             ext_funcs: PrimaryMap::new(),
+            values_labels: None,
         }
     }
 
     /// Clear everything.
     pub fn clear(&mut self) {
         self.insts.clear();
         self.results.clear();
         self.ebbs.clear();
         self.value_lists.clear();
         self.values.clear();
         self.signatures.clear();
         self.ext_funcs.clear();
+        self.values_labels = None;
     }
 
     /// Get the total number of instructions created in this function, whether they are currently
     /// inserted in the layout or not.
     ///
     /// This is intended for use with `SecondaryMap::with_capacity`.
     pub fn num_insts(&self) -> usize {
         self.insts.len()
@@ -112,16 +121,23 @@ impl DataFlowGraph {
     pub fn ebb_is_valid(&self, ebb: Ebb) -> bool {
         self.ebbs.is_valid(ebb)
     }
 
     /// Get the total number of values.
     pub fn num_values(&self) -> usize {
         self.values.len()
     }
+
+    /// Starts collection of debug information.
+    pub fn collect_debug_info(&mut self) {
+        if self.values_labels.is_none() {
+            self.values_labels = Some(HashMap::new());
+        }
+    }
 }
 
 /// Resolve value aliases.
 ///
 /// Find the original SSA value that `value` aliases, or None if an
 /// alias cycle is detected.
 fn maybe_resolve_aliases(values: &PrimaryMap<Value, ValueData>, value: Value) -> Option<Value> {
     let mut v = value;
--- a/third_party/rust/cranelift-codegen/src/ir/function.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/function.rs
@@ -10,16 +10,17 @@ use crate::ir::{DataFlowGraph, ExternalN
 use crate::ir::{
     Ebb, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap, HeapData, JumpTable,
     JumpTableData, SigRef, StackSlot, StackSlotData, Table, TableData,
 };
 use crate::ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations};
 use crate::ir::{JumpTableOffsets, JumpTables};
 use crate::isa::{CallConv, EncInfo, Encoding, Legalize, TargetIsa};
 use crate::regalloc::RegDiversions;
+use crate::value_label::ValueLabelsRanges;
 use crate::write::write_function;
 use core::fmt;
 
 /// A function.
 ///
 /// Functions can be cloned, but it is not a very fast operation.
 /// The clone will have all the same entity numbers as the original.
 #[derive(Clone)]
@@ -150,17 +151,25 @@ impl Function {
 
     /// Declares a table accessible to the function.
     pub fn create_table(&mut self, data: TableData) -> Table {
         self.tables.push(data)
     }
 
     /// Return an object that can display this function with correct ISA-specific annotations.
     pub fn display<'a, I: Into<Option<&'a TargetIsa>>>(&'a self, isa: I) -> DisplayFunction<'a> {
-        DisplayFunction(self, isa.into())
+        DisplayFunction(self, isa.into().into())
+    }
+
+    /// Return an object that can display this function with correct ISA-specific annotations.
+    pub fn display_with<'a>(
+        &'a self,
+        annotations: DisplayFunctionAnnotations<'a>,
+    ) -> DisplayFunction<'a> {
+        DisplayFunction(self, annotations)
     }
 
     /// Find a presumed unique special-purpose function parameter value.
     ///
     /// Returns the value of the last `purpose` parameter, or `None` if no such parameter exists.
     pub fn special_param(&self, purpose: ir::ArgumentPurpose) -> Option<ir::Value> {
         let entry = self.layout.entry_block().expect("Function is empty");
         self.signature
@@ -197,36 +206,68 @@ impl Function {
         self.encode(inst, isa).map(|e| self.encodings[inst] = e)
     }
 
     /// Wrapper around `TargetIsa::encode` for encoding an existing instruction
     /// in the `Function`.
     pub fn encode(&self, inst: ir::Inst, isa: &TargetIsa) -> Result<Encoding, Legalize> {
         isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst))
     }
+
+    /// Starts collection of debug information.
+    pub fn collect_debug_info(&mut self) {
+        self.dfg.collect_debug_info();
+    }
+}
+
+/// Additional annotations for function display.
+pub struct DisplayFunctionAnnotations<'a> {
+    /// Enable ISA annotations.
+    pub isa: Option<&'a TargetIsa>,
+
+    /// Enable value labels annotations.
+    pub value_ranges: Option<&'a ValueLabelsRanges>,
+}
+
+impl<'a> DisplayFunctionAnnotations<'a> {
+    fn default() -> Self {
+        DisplayFunctionAnnotations {
+            isa: None,
+            value_ranges: None,
+        }
+    }
+}
+
+impl<'a> From<Option<&'a TargetIsa>> for DisplayFunctionAnnotations<'a> {
+    fn from(isa: Option<&'a TargetIsa>) -> DisplayFunctionAnnotations {
+        DisplayFunctionAnnotations {
+            isa,
+            value_ranges: None,
+        }
+    }
 }
 
 /// Wrapper type capable of displaying a `Function` with correct ISA annotations.
-pub struct DisplayFunction<'a>(&'a Function, Option<&'a TargetIsa>);
+pub struct DisplayFunction<'a>(&'a Function, DisplayFunctionAnnotations<'a>);
 
 impl<'a> fmt::Display for DisplayFunction<'a> {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write_function(fmt, self.0, self.1)
+        write_function(fmt, self.0, &self.1)
     }
 }
 
 impl fmt::Display for Function {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write_function(fmt, self, None)
+        write_function(fmt, self, &DisplayFunctionAnnotations::default())
     }
 }
 
 impl fmt::Debug for Function {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write_function(fmt, self, None)
+        write_function(fmt, self, &DisplayFunctionAnnotations::default())
     }
 }
 
 /// Iterator returning instruction offsets and sizes: `(offset, inst, size)`.
 pub struct InstOffsetIter<'a> {
     encinfo: EncInfo,
     divert: RegDiversions,
     func: &'a Function,
--- a/third_party/rust/cranelift-codegen/src/ir/mod.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/mod.rs
@@ -27,17 +27,17 @@ pub use crate::ir::builder::{InsertBuild
 pub use crate::ir::dfg::{DataFlowGraph, ValueDef};
 pub use crate::ir::entities::{
     Ebb, FuncRef, GlobalValue, Heap, Inst, JumpTable, SigRef, StackSlot, Table, Value,
 };
 pub use crate::ir::extfunc::{
     AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature,
 };
 pub use crate::ir::extname::ExternalName;
-pub use crate::ir::function::Function;
+pub use crate::ir::function::{DisplayFunctionAnnotations, Function};
 pub use crate::ir::globalvalue::GlobalValueData;
 pub use crate::ir::heap::{HeapData, HeapStyle};
 pub use crate::ir::instructions::{
     InstructionData, Opcode, ValueList, ValueListPool, VariableArgs,
 };
 pub use crate::ir::jumptable::JumpTableData;
 pub use crate::ir::layout::Layout;
 pub use crate::ir::libcall::{get_libcall_funcref, get_probestack_funcref, LibCall};
@@ -46,17 +46,17 @@ pub use crate::ir::progpoint::{ExpandedP
 pub use crate::ir::sourceloc::SourceLoc;
 pub use crate::ir::stackslot::{StackSlotData, StackSlotKind, StackSlots};
 pub use crate::ir::table::TableData;
 pub use crate::ir::trapcode::TrapCode;
 pub use crate::ir::types::Type;
 pub use crate::ir::valueloc::{ArgumentLoc, ValueLoc};
 
 use crate::binemit;
-use crate::entity::{PrimaryMap, SecondaryMap};
+use crate::entity::{entity_impl, PrimaryMap, SecondaryMap};
 use crate::isa;
 
 /// Map of value locations.
 pub type ValueLocations = SecondaryMap<Value, ValueLoc>;
 
 /// Map of jump tables.
 pub type JumpTables = PrimaryMap<JumpTable, JumpTableData>;
 
@@ -66,8 +66,39 @@ pub type InstEncodings = SecondaryMap<In
 /// Code offsets for EBBs.
 pub type EbbOffsets = SecondaryMap<Ebb, binemit::CodeOffset>;
 
 /// Code offsets for Jump Tables.
 pub type JumpTableOffsets = SecondaryMap<JumpTable, binemit::CodeOffset>;
 
 /// Source locations for instructions.
 pub type SourceLocs = SecondaryMap<Inst, SourceLoc>;
+
+/// Marked with a label value.
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct ValueLabel(u32);
+entity_impl!(ValueLabel, "val");
+
+/// A label of a Value.
+#[derive(Debug, Clone)]
+pub struct ValueLabelStart {
+    /// Source location when it is in effect
+    pub from: SourceLoc,
+
+    /// The label index.
+    pub label: ValueLabel,
+}
+
+/// Value label assignements: label starts or value aliases.
+#[derive(Debug, Clone)]
+pub enum ValueLabelAssignments {
+    /// Original value labels assigned at transform.
+    Starts(std::vec::Vec<ValueLabelStart>),
+
+    /// A value alias to original value.
+    Alias {
+        /// Source location when it is in effect
+        from: SourceLoc,
+
+        /// The label index.
+        value: Value,
+    },
+}
--- a/third_party/rust/cranelift-codegen/src/isa/constraints.rs
+++ b/third_party/rust/cranelift-codegen/src/isa/constraints.rs
@@ -100,23 +100,23 @@ pub struct RecipeConstraints {
     pub ins: &'static [OperandConstraint],
 
     /// Constraints for the instruction's fixed results.
     ///
     /// If the instruction produces a variable number of results, it's probably a call and the
     /// constraints must be derived from the calling convention ABI.
     pub outs: &'static [OperandConstraint],
 
-    /// Are any of the input constraints `FixedReg`?
+    /// Are any of the input constraints `FixedReg` or `FixedTied`?
     pub fixed_ins: bool,
 
-    /// Are any of the output constraints `FixedReg`?
+    /// Are any of the output constraints `FixedReg` or `FixedTied`?
     pub fixed_outs: bool,
 
-    /// Are there any tied operands?
+    /// Are any of the input/output constraints `Tied` (but not `FixedTied`)?
     pub tied_ops: bool,
 
     /// Does this instruction clobber the CPU flags?
     ///
     /// When true, SSA values of type `iflags` or `fflags` can not be live across the instruction.
     pub clobbers_flags: bool,
 }
 
--- a/third_party/rust/cranelift-codegen/src/isa/x86/enc_tables.rs
+++ b/third_party/rust/cranelift-codegen/src/isa/x86/enc_tables.rs
@@ -1,15 +1,15 @@
 //! Encoding tables for x86 ISAs.
 
 use super::registers::*;
 use crate::bitset::BitSet;
 use crate::cursor::{Cursor, FuncCursor};
 use crate::flowgraph::ControlFlowGraph;
-use crate::ir::condcodes::IntCC;
+use crate::ir::condcodes::{FloatCC, IntCC};
 use crate::ir::{self, Function, Inst, InstBuilder};
 use crate::isa;
 use crate::isa::constraints::*;
 use crate::isa::enc_tables::*;
 use crate::isa::encoding::base_size;
 use crate::isa::encoding::RecipeSizing;
 use crate::isa::RegUnit;
 use crate::regalloc::RegDiversions;
@@ -86,16 +86,35 @@ fn size_plus_maybe_sib_or_offset_for_in_
     sizing: &RecipeSizing,
     inst: Inst,
     divert: &RegDiversions,
     func: &Function,
 ) -> u8 {
     sizing.base_size + additional_size_if(1, inst, divert, func, needs_sib_byte_or_offset)
 }
 
+/// If the value's definition is a constant immediate, returns its unpacked value, or None
+/// otherwise.
+fn maybe_iconst_imm(pos: &FuncCursor, value: ir::Value) -> Option<i64> {
+    if let ir::ValueDef::Result(inst, _) = &pos.func.dfg.value_def(value) {
+        if let ir::InstructionData::UnaryImm {
+            opcode: ir::Opcode::Iconst,
+            imm,
+        } = &pos.func.dfg[*inst]
+        {
+            let value: i64 = (*imm).into();
+            Some(value)
+        } else {
+            None
+        }
+    } else {
+        None
+    }
+}
+
 /// Expand the `sdiv` and `srem` instructions using `x86_sdivmodx`.
 fn expand_sdivrem(
     inst: ir::Inst,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
     isa: &isa::TargetIsa,
 ) {
     let (x, y, is_srem) = match func.dfg[inst] {
@@ -104,51 +123,74 @@ fn expand_sdivrem(
             args,
         } => (args[0], args[1], false),
         ir::InstructionData::Binary {
             opcode: ir::Opcode::Srem,
             args,
         } => (args[0], args[1], true),
         _ => panic!("Need sdiv/srem: {}", func.dfg.display_inst(inst, None)),
     };
-    let avoid_div_traps = isa.flags().avoid_div_traps();
+
     let old_ebb = func.layout.pp_ebb(inst);
     let result = func.dfg.first_result(inst);
     let ty = func.dfg.value_type(result);
 
     let mut pos = FuncCursor::new(func).at_inst(inst);
     pos.use_srcloc(inst);
     pos.func.dfg.clear_results(inst);
 
+    let avoid_div_traps = isa.flags().avoid_div_traps();
+
     // If we can tolerate native division traps, sdiv doesn't need branching.
     if !avoid_div_traps && !is_srem {
         let xhi = pos.ins().sshr_imm(x, i64::from(ty.lane_bits()) - 1);
         pos.ins().with_result(result).x86_sdivmodx(x, xhi, y);
         pos.remove_inst();
         return;
     }
 
+    // Try to remove checks if the input value is an immediate other than 0 or -1. For these two
+    // immediates, we'd ideally replace conditional traps by traps, but this requires more
+    // manipulation of the dfg/cfg, which is out of scope here.
+    let (could_be_zero, could_be_minus_one) = if let Some(imm) = maybe_iconst_imm(&pos, y) {
+        (imm == 0, imm == -1)
+    } else {
+        (true, true)
+    };
+
+    // Put in an explicit division-by-zero trap if the environment requires it.
+    if avoid_div_traps && could_be_zero {
+        pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
+    }
+
+    if !could_be_minus_one {
+        let xhi = pos.ins().sshr_imm(x, i64::from(ty.lane_bits()) - 1);
+        let reuse = if is_srem {
+            [None, Some(result)]
+        } else {
+            [Some(result), None]
+        };
+        pos.ins().with_results(reuse).x86_sdivmodx(x, xhi, y);
+        pos.remove_inst();
+        return;
+    }
+
     // EBB handling the -1 divisor case.
     let minus_one = pos.func.dfg.make_ebb();
 
     // Final EBB with one argument representing the final result value.
     let done = pos.func.dfg.make_ebb();
 
     // Move the `inst` result value onto the `done` EBB.
     pos.func.dfg.attach_ebb_param(done, result);
 
     // Start by checking for a -1 divisor which needs to be handled specially.
     let is_m1 = pos.ins().ifcmp_imm(y, -1);
     pos.ins().brif(IntCC::Equal, is_m1, minus_one, &[]);
 
-    // Put in an explicit division-by-zero trap if the environment requires it.
-    if avoid_div_traps {
-        pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
-    }
-
     // Now it is safe to execute the `x86_sdivmodx` instruction which will still trap on division
     // by zero.
     let xhi = pos.ins().sshr_imm(x, i64::from(ty.lane_bits()) - 1);
     let (quot, rem) = pos.ins().x86_sdivmodx(x, xhi, y);
     let divres = if is_srem { rem } else { quot };
     pos.ins().jump(done, &[divres]);
 
     // Now deal with the -1 divisor case.
@@ -201,17 +243,27 @@ fn expand_udivrem(
     let ty = func.dfg.value_type(result);
 
     let mut pos = FuncCursor::new(func).at_inst(inst);
     pos.use_srcloc(inst);
     pos.func.dfg.clear_results(inst);
 
     // Put in an explicit division-by-zero trap if the environment requires it.
     if avoid_div_traps {
-        pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
+        let zero_check = if let Some(imm) = maybe_iconst_imm(&pos, y) {
+            // Ideally, we'd just replace the conditional trap with a trap when the immediate is
+            // zero, but this requires more manipulation of the dfg/cfg, which is out of scope
+            // here.
+            imm == 0
+        } else {
+            true
+        };
+        if zero_check {
+            pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
+        }
     }
 
     // Now it is safe to execute the `x86_udivmodx` instruction.
     let xhi = pos.ins().iconst(ty, 0);
     let reuse = if is_urem {
         [None, Some(result)]
     } else {
         [Some(result), None]
@@ -223,18 +275,16 @@ fn expand_udivrem(
 /// Expand the `fmin` and `fmax` instructions using the x86 `x86_fmin` and `x86_fmax`
 /// instructions.
 fn expand_minmax(
     inst: ir::Inst,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
     _isa: &isa::TargetIsa,
 ) {
-    use crate::ir::condcodes::FloatCC;
-
     let (x, y, x86_opc, bitwise_opc) = match func.dfg[inst] {
         ir::InstructionData::Binary {
             opcode: ir::Opcode::Fmin,
             args,
         } => (args[0], args[1], ir::Opcode::X86Fmin, ir::Opcode::Bor),
         ir::InstructionData::Binary {
             opcode: ir::Opcode::Fmax,
             args,
@@ -317,18 +367,16 @@ fn expand_minmax(
 /// x86 has no unsigned-to-float conversions. We handle the easy case of zero-extending i32 to
 /// i64 with a pattern, the rest needs more code.
 fn expand_fcvt_from_uint(
     inst: ir::Inst,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
     _isa: &isa::TargetIsa,
 ) {
-    use crate::ir::condcodes::IntCC;
-
     let x;
     match func.dfg[inst] {
         ir::InstructionData::Unary {
             opcode: ir::Opcode::FcvtFromUint,
             arg,
         } => x = arg,
         _ => panic!("Need fcvt_from_uint: {}", func.dfg.display_inst(inst, None)),
     }
@@ -390,17 +438,16 @@ fn expand_fcvt_from_uint(
 }
 
 fn expand_fcvt_to_sint(
     inst: ir::Inst,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
     _isa: &isa::TargetIsa,
 ) {
-    use crate::ir::condcodes::{FloatCC, IntCC};
     use crate::ir::immediates::{Ieee32, Ieee64};
 
     let x = match func.dfg[inst] {
         ir::InstructionData::Unary {
             opcode: ir::Opcode::FcvtToSint,
             arg,
         } => arg,
         _ => panic!("Need fcvt_to_sint: {}", func.dfg.display_inst(inst, None)),
@@ -486,17 +533,16 @@ fn expand_fcvt_to_sint(
 }
 
 fn expand_fcvt_to_sint_sat(
     inst: ir::Inst,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
     _isa: &isa::TargetIsa,
 ) {
-    use crate::ir::condcodes::{FloatCC, IntCC};
     use crate::ir::immediates::{Ieee32, Ieee64};
 
     let x = match func.dfg[inst] {
         ir::InstructionData::Unary {
             opcode: ir::Opcode::FcvtToSintSat,
             arg,
         } => arg,
         _ => panic!(
@@ -606,17 +652,16 @@ fn expand_fcvt_to_sint_sat(
 }
 
 fn expand_fcvt_to_uint(
     inst: ir::Inst,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
     _isa: &isa::TargetIsa,
 ) {
-    use crate::ir::condcodes::{FloatCC, IntCC};
     use crate::ir::immediates::{Ieee32, Ieee64};
 
     let x = match func.dfg[inst] {
         ir::InstructionData::Unary {
             opcode: ir::Opcode::FcvtToUint,
             arg,
         } => arg,
         _ => panic!("Need fcvt_to_uint: {}", func.dfg.display_inst(inst, None)),
@@ -688,17 +733,16 @@ fn expand_fcvt_to_uint(
 }
 
 fn expand_fcvt_to_uint_sat(
     inst: ir::Inst,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
     _isa: &isa::TargetIsa,
 ) {
-    use crate::ir::condcodes::{FloatCC, IntCC};
     use crate::ir::immediates::{Ieee32, Ieee64};
 
     let x = match func.dfg[inst] {
         ir::InstructionData::Unary {
             opcode: ir::Opcode::FcvtToUintSat,
             arg,
         } => arg,
         _ => panic!(
--- a/third_party/rust/cranelift-codegen/src/legalizer/heap.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/heap.rs
@@ -146,16 +146,26 @@ fn compute_addr(
     offset_ty: ir::Type,
     func: &mut ir::Function,
 ) {
     let mut pos = FuncCursor::new(func).at_inst(inst);
     pos.use_srcloc(inst);
 
     // Convert `offset` to `addr_ty`.
     if offset_ty != addr_ty {
+        let labels_value = offset;
         offset = pos.ins().uextend(addr_ty, offset);
+        if let Some(values_labels) = pos.func.dfg.values_labels.as_mut() {
+            values_labels.insert(
+                offset,
+                ir::ValueLabelAssignments::Alias {
+                    from: pos.func.srclocs[inst],
+                    value: labels_value,
+                },
+            );
+        }
     }
 
     // Add the heap base address base
     let base_gv = pos.func.heaps[heap].base;
     let base = pos.ins().global_value(addr_ty, base_gv);
     pos.func.dfg.replace(inst).iadd(base, offset);
 }
--- a/third_party/rust/cranelift-codegen/src/lib.rs
+++ b/third_party/rust/cranelift-codegen/src/lib.rs
@@ -52,16 +52,17 @@ extern crate std;
 
 #[cfg(not(feature = "std"))]
 use hashmap_core::{map as hash_map, HashMap, HashSet};
 #[cfg(feature = "std")]
 use std::collections::{hash_map, HashMap, HashSet};
 
 pub use crate::context::Context;
 pub use crate::legalizer::legalize_function;
+pub use crate::value_label::ValueLabelsRanges;
 pub use crate::verifier::verify_function;
 pub use crate::write::write_function;
 
 pub use cranelift_bforest as bforest;
 pub use cranelift_entity as entity;
 
 pub mod binemit;
 pub mod cfg_printer;
@@ -98,13 +99,14 @@ mod ref_slice;
 mod regalloc;
 mod result;
 mod scoped_hash_map;
 mod simple_gvn;
 mod simple_preopt;
 mod stack_layout;
 mod topo_order;
 mod unreachable_code;
+mod value_label;
 
 pub use crate::result::{CodegenError, CodegenResult};
 
 /// Version number of this crate.
 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
--- a/third_party/rust/cranelift-codegen/src/print_errors.rs
+++ b/third_party/rust/cranelift-codegen/src/print_errors.rs
@@ -24,17 +24,17 @@ pub fn pretty_verifier_error<'a>(
     let mut errors = errors.0;
     let mut w = String::new();
     let num_errors = errors.len();
 
     decorate_function(
         &mut PrettyVerifierError(func_w.unwrap_or_else(|| Box::new(PlainWriter)), &mut errors),
         &mut w,
         func,
-        isa,
+        &isa.into(),
     )
     .unwrap();
 
     writeln!(
         w,
         "\n; {} verifier error{} detected (see above). Compilation aborted.",
         num_errors,
         if num_errors == 1 { "" } else { "s" }
--- a/third_party/rust/cranelift-codegen/src/regalloc/context.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/context.rs
@@ -59,16 +59,21 @@ impl Context {
         self.coalescing.clear();
         self.topo.clear();
         self.tracker.clear();
         self.spilling.clear();
         self.reload.clear();
         self.coloring.clear();
     }
 
+    /// Current values liveness state.
+    pub fn liveness(&self) -> &Liveness {
+        &self.liveness
+    }
+
     /// Allocate registers in `func`.
     ///
     /// After register allocation, all values in `func` have been assigned to a register or stack
     /// location that is consistent with instruction encoding constraints.
     pub fn run(
         &mut self,
         isa: &TargetIsa,
         func: &mut Function,
--- a/third_party/rust/cranelift-codegen/src/regalloc/liveness.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/liveness.rs
@@ -309,16 +309,21 @@ impl Liveness {
     pub fn new() -> Self {
         Self {
             ranges: LiveRangeSet::new(),
             forest: LiveRangeForest::new(),
             worklist: Vec::new(),
         }
     }
 
+    /// Current live ranges.
+    pub fn ranges(&self) -> &LiveRangeSet {
+        &self.ranges
+    }
+
     /// Get a context needed for working with a `LiveRange`.
     pub fn context<'a>(&'a self, layout: &'a Layout) -> LiveRangeContext<'a, Layout> {
         LiveRangeContext::new(layout, &self.forest)
     }
 
     /// Clear all data structures in this liveness analysis.
     pub fn clear(&mut self) {
         self.ranges.clear();
--- a/third_party/rust/cranelift-codegen/src/settings.rs
+++ b/third_party/rust/cranelift-codegen/src/settings.rs
@@ -374,29 +374,29 @@ mod tests {
     #[test]
     fn display_default() {
         let b = builder();
         let f = Flags::new(b);
         assert_eq!(
             f.to_string(),
             "[shared]\n\
              opt_level = \"default\"\n\
+             baldrdash_prologue_words = 0\n\
+             probestack_size_log2 = 12\n\
              enable_verifier = true\n\
              is_pic = false\n\
              colocated_libcalls = false\n\
              avoid_div_traps = false\n\
              enable_float = true\n\
              enable_nan_canonicalization = false\n\
              enable_simd = true\n\
              enable_atomics = true\n\
-             baldrdash_prologue_words = 0\n\
              allones_funcaddrs = false\n\
              probestack_enabled = true\n\
              probestack_func_adjusts_sp = false\n\
-             probestack_size_log2 = 12\n\
              jump_tables_enabled = true\n"
         );
         assert_eq!(f.opt_level(), super::OptLevel::Default);
         assert_eq!(f.enable_simd(), true);
         assert_eq!(f.baldrdash_prologue_words(), 0);
     }
 
     #[test]
--- a/third_party/rust/cranelift-codegen/src/simple_preopt.rs
+++ b/third_party/rust/cranelift-codegen/src/simple_preopt.rs
@@ -1,16 +1,14 @@
 //! A pre-legalization rewriting pass.
 //!
 //! This module provides early-stage optimizations. The optimizations found
 //! should be useful for already well-optimized code. More general purpose
 //! early-stage optimizations can be found in the preopt crate.
 
-#![allow(non_snake_case)]
-
 use crate::cursor::{Cursor, FuncCursor};
 use crate::divconst_magic_numbers::{magic_s32, magic_s64, magic_u32, magic_u64};
 use crate::divconst_magic_numbers::{MS32, MS64, MU32, MU64};
 use crate::flowgraph::ControlFlowGraph;
 use crate::ir::condcodes::{CondCode, IntCC};
 use crate::ir::dfg::ValueDef;
 use crate::ir::instructions::{Opcode, ValueList};
 use crate::ir::types::{I32, I64};
@@ -22,132 +20,138 @@ use crate::timing;
 //
 // Pattern-match helpers and transformation for div and rem by constants.
 
 // Simple math helpers
 
 /// if `x` is a power of two, or the negation thereof, return the power along
 /// with a boolean that indicates whether `x` is negative. Else return None.
 #[inline]
-fn isPowerOf2_S32(x: i32) -> Option<(bool, u32)> {
+fn i32_is_power_of_two(x: i32) -> Option<(bool, u32)> {
     // We have to special-case this because abs(x) isn't representable.
     if x == -0x8000_0000 {
         return Some((true, 31));
     }
     let abs_x = i32::wrapping_abs(x) as u32;
     if abs_x.is_power_of_two() {
         return Some((x < 0, abs_x.trailing_zeros()));
     }
     None
 }
 
-/// Same comments as for isPowerOf2_S64 apply.
+/// Same comments as for i32_is_power_of_two apply.
 #[inline]
-fn isPowerOf2_S64(x: i64) -> Option<(bool, u32)> {
+fn i64_is_power_of_two(x: i64) -> Option<(bool, u32)> {
     // We have to special-case this because abs(x) isn't representable.
     if x == -0x8000_0000_0000_0000 {
         return Some((true, 63));
     }
     let abs_x = i64::wrapping_abs(x) as u64;
     if abs_x.is_power_of_two() {
         return Some((x < 0, abs_x.trailing_zeros()));
     }
     None
 }
 
+/// Representation of an instruction that can be replaced by a single division/remainder operation
+/// between a left Value operand and a right immediate operand.
 #[derive(Debug)]
 enum DivRemByConstInfo {
-    DivU32(Value, u32), // In all cases, the arguments are:
-    DivU64(Value, u64), // left operand, right operand
+    DivU32(Value, u32),
+    DivU64(Value, u64),
     DivS32(Value, i32),
     DivS64(Value, i64),
     RemU32(Value, u32),
     RemU64(Value, u64),
     RemS32(Value, i32),
     RemS64(Value, i64),
 }
 
-/// Possibly create a DivRemByConstInfo from the given components, by
-/// figuring out which, if any, of the 8 cases apply, and also taking care to
-/// sanity-check the immediate.
+/// Possibly create a DivRemByConstInfo from the given components, by figuring out which, if any,
+/// of the 8 cases apply, and also taking care to sanity-check the immediate.
 fn package_up_divrem_info(
-    argL: Value,
-    argL_ty: Type,
-    argRs: i64,
-    isSigned: bool,
-    isRem: bool,
+    value: Value,
+    value_type: Type,
+    imm_i64: i64,
+    is_signed: bool,
+    is_rem: bool,
 ) -> Option<DivRemByConstInfo> {
-    let argRu: u64 = argRs as u64;
-    if !isSigned && argL_ty == I32 && argRu < 0x1_0000_0000 {
-        let con = if isRem {
-            DivRemByConstInfo::RemU32
-        } else {
-            DivRemByConstInfo::DivU32
-        };
-        return Some(con(argL, argRu as u32));
+    let imm_u64 = imm_i64 as u64;
+
+    match (is_signed, value_type) {
+        (false, I32) => {
+            if imm_u64 < 0x1_0000_0000 {
+                if is_rem {
+                    Some(DivRemByConstInfo::RemU32(value, imm_u64 as u32))
+                } else {
+                    Some(DivRemByConstInfo::DivU32(value, imm_u64 as u32))
+                }
+            } else {
+                None
+            }
+        }
+
+        (false, I64) => {
+            // unsigned 64, no range constraint.
+            if is_rem {
+                Some(DivRemByConstInfo::RemU64(value, imm_u64))
+            } else {
+                Some(DivRemByConstInfo::DivU64(value, imm_u64))
+            }
+        }
+
+        (true, I32) => {
+            if imm_u64 <= 0x7fff_ffff || imm_u64 >= 0xffff_ffff_8000_0000 {
+                if is_rem {
+                    Some(DivRemByConstInfo::RemS32(value, imm_u64 as i32))
+                } else {
+                    Some(DivRemByConstInfo::DivS32(value, imm_u64 as i32))
+                }
+            } else {
+                None
+            }
+        }
+
+        (true, I64) => {
+            // signed 64, no range constraint.
+            if is_rem {
+                Some(DivRemByConstInfo::RemS64(value, imm_u64 as i64))
+            } else {
+                Some(DivRemByConstInfo::DivS64(value, imm_u64 as i64))
+            }
+        }
+
+        _ => None,
     }
-    if !isSigned && argL_ty == I64 {
-        // unsigned 64, no range constraint
-        let con = if isRem {
-            DivRemByConstInfo::RemU64
-        } else {
-            DivRemByConstInfo::DivU64
-        };
-        return Some(con(argL, argRu));
-    }
-    if isSigned && argL_ty == I32 && (argRu <= 0x7fff_ffff || argRu >= 0xffff_ffff_8000_0000) {
-        let con = if isRem {
-            DivRemByConstInfo::RemS32
-        } else {
-            DivRemByConstInfo::DivS32
-        };
-        return Some(con(argL, argRu as i32));
-    }
-    if isSigned && argL_ty == I64 {
-        // signed 64, no range constraint
-        let con = if isRem {
-            DivRemByConstInfo::RemS64
-        } else {
-            DivRemByConstInfo::DivS64
-        };
-        return Some(con(argL, argRu as i64));
-    }
-    None
 }
 
-/// Examine `idata` to see if it is a div or rem by a constant, and if so
-/// return the operands, signedness, operation size and div-vs-rem-ness in a
-/// handy bundle.
+/// Examine `inst` to see if it is a div or rem by a constant, and if so return the operands,
+/// signedness, operation size and div-vs-rem-ness in a handy bundle.
 fn get_div_info(inst: Inst, dfg: &DataFlowGraph) -> Option<DivRemByConstInfo> {
-    let idata: &InstructionData = &dfg[inst];
-
-    if let InstructionData::BinaryImm { opcode, arg, imm } = *idata {
-        let (isSigned, isRem) = match opcode {
+    if let InstructionData::BinaryImm { opcode, arg, imm } = dfg[inst] {
+        let (is_signed, is_rem) = match opcode {
             Opcode::UdivImm => (false, false),
             Opcode::UremImm => (false, true),
             Opcode::SdivImm => (true, false),
             Opcode::SremImm => (true, true),
-            _other => return None,
+            _ => return None,
         };
-        // Pull the operation size (type) from the left arg
-        let argL_ty = dfg.value_type(arg);
-        return package_up_divrem_info(arg, argL_ty, imm.into(), isSigned, isRem);
+        return package_up_divrem_info(arg, dfg.value_type(arg), imm.into(), is_signed, is_rem);
     }
 
     None
 }
 
-/// Actually do the transformation given a bundle containing the relevant
-/// information. `divrem_info` describes a div or rem by a constant, that
-/// `pos` currently points at, and `inst` is the associated instruction.
-/// `inst` is replaced by a sequence of other operations that calculate the
-/// same result. Note that there are various `divrem_info` cases where we
-/// cannot do any transformation, in which case `inst` is left unchanged.
+/// Actually do the transformation given a bundle containing the relevant information.
+/// `divrem_info` describes a div or rem by a constant, that `pos` currently points at, and `inst`
+/// is the associated instruction.  `inst` is replaced by a sequence of other operations that
+/// calculate the same result. Note that there are various `divrem_info` cases where we cannot do
+/// any transformation, in which case `inst` is left unchanged.
 fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCursor, inst: Inst) {
-    let isRem = match *divrem_info {
+    let is_rem = match *divrem_info {
         DivRemByConstInfo::DivU32(_, _)
         | DivRemByConstInfo::DivU64(_, _)
         | DivRemByConstInfo::DivS32(_, _)
         | DivRemByConstInfo::DivS64(_, _) => false,
         DivRemByConstInfo::RemU32(_, _)
         | DivRemByConstInfo::RemU64(_, _)
         | DivRemByConstInfo::RemS32(_, _)
         | DivRemByConstInfo::RemS64(_, _) => true,
@@ -157,32 +161,32 @@ fn do_divrem_transformation(divrem_info:
         // -------------------- U32 --------------------
 
         // U32 div, rem by zero: ignore
         DivRemByConstInfo::DivU32(_n1, 0) | DivRemByConstInfo::RemU32(_n1, 0) => {}
 
         // U32 div by 1: identity
         // U32 rem by 1: zero
         DivRemByConstInfo::DivU32(n1, 1) | DivRemByConstInfo::RemU32(n1, 1) => {
-            if isRem {
+            if is_rem {
                 pos.func.dfg.replace(inst).iconst(I32, 0);
             } else {
                 pos.func.dfg.replace(inst).copy(n1);
             }
         }
 
         // U32 div, rem by a power-of-2
         DivRemByConstInfo::DivU32(n1, d) | DivRemByConstInfo::RemU32(n1, d)
             if d.is_power_of_two() =>
         {
             debug_assert!(d >= 2);
             // compute k where d == 2^k
             let k = d.trailing_zeros();
             debug_assert!(k >= 1 && k <= 31);
-            if isRem {
+            if is_rem {
                 let mask = (1u64 << k) - 1;
                 pos.func.dfg.replace(inst).band_imm(n1, mask as i64);
             } else {
                 pos.func.dfg.replace(inst).ushr_imm(n1, k as i64);
             }
         }
 
         // U32 div, rem by non-power-of-2
@@ -211,48 +215,48 @@ fn do_divrem_transformation(divrem_info:
                 if shift_by > 0 {
                     qf = pos.ins().ushr_imm(q1, shift_by as i64);
                 } else {
                     qf = q1;
                 }
             }
             // Now qf holds the final quotient. If necessary calculate the
             // remainder instead.
-            if isRem {
+            if is_rem {
                 let tt = pos.ins().imul_imm(qf, d as i64);
                 pos.func.dfg.replace(inst).isub(n1, tt);
             } else {
                 pos.func.dfg.replace(inst).copy(qf);
             }
         }
 
         // -------------------- U64 --------------------
 
         // U64 div, rem by zero: ignore
         DivRemByConstInfo::DivU64(_n1, 0) | DivRemByConstInfo::RemU64(_n1, 0) => {}
 
         // U64 div by 1: identity
         // U64 rem by 1: zero
         DivRemByConstInfo::DivU64(n1, 1) | DivRemByConstInfo::RemU64(n1, 1) => {
-            if isRem {
+            if is_rem {
                 pos.func.dfg.replace(inst).iconst(I64, 0);
             } else {
                 pos.func.dfg.replace(inst).copy(n1);
             }
         }
 
         // U64 div, rem by a power-of-2
         DivRemByConstInfo::DivU64(n1, d) | DivRemByConstInfo::RemU64(n1, d)
             if d.is_power_of_two() =>
         {
             debug_assert!(d >= 2);
             // compute k where d == 2^k
             let k = d.trailing_zeros();
             debug_assert!(k >= 1 && k <= 63);
-            if isRem {
+            if is_rem {
                 let mask = (1u64 << k) - 1;
                 pos.func.dfg.replace(inst).band_imm(n1, mask as i64);
             } else {
                 pos.func.dfg.replace(inst).ushr_imm(n1, k as i64);
             }
         }
 
         // U64 div, rem by non-power-of-2
@@ -281,17 +285,17 @@ fn do_divrem_transformation(divrem_info:
                 if shift_by > 0 {
                     qf = pos.ins().ushr_imm(q1, shift_by as i64);
                 } else {
                     qf = q1;
                 }
             }
             // Now qf holds the final quotient. If necessary calculate the
             // remainder instead.
-            if isRem {
+            if is_rem {
                 let tt = pos.ins().imul_imm(qf, d as i64);
                 pos.func.dfg.replace(inst).isub(n1, tt);
             } else {
                 pos.func.dfg.replace(inst).copy(qf);
             }
         }
 
         // -------------------- S32 --------------------
@@ -300,43 +304,43 @@ fn do_divrem_transformation(divrem_info:
         DivRemByConstInfo::DivS32(_n1, -1)
         | DivRemByConstInfo::RemS32(_n1, -1)
         | DivRemByConstInfo::DivS32(_n1, 0)
         | DivRemByConstInfo::RemS32(_n1, 0) => {}
 
         // S32 div by 1: identity
         // S32 rem by 1: zero
         DivRemByConstInfo::DivS32(n1, 1) | DivRemByConstInfo::RemS32(n1, 1) => {
-            if isRem {
+            if is_rem {
                 pos.func.dfg.replace(inst).iconst(I32, 0);
             } else {
                 pos.func.dfg.replace(inst).copy(n1);
             }
         }
 
         DivRemByConstInfo::DivS32(n1, d) | DivRemByConstInfo::RemS32(n1, d) => {
-            if let Some((isNeg, k)) = isPowerOf2_S32(d) {
+            if let Some((is_negative, k)) = i32_is_power_of_two(d) {
                 // k can be 31 only in the case that d is -2^31.
                 debug_assert!(k >= 1 && k <= 31);
                 let t1 = if k - 1 == 0 {
                     n1
                 } else {
                     pos.ins().sshr_imm(n1, (k - 1) as i64)
                 };
                 let t2 = pos.ins().ushr_imm(t1, (32 - k) as i64);
                 let t3 = pos.ins().iadd(n1, t2);
-                if isRem {
+                if is_rem {
                     // S32 rem by a power-of-2
                     let t4 = pos.ins().band_imm(t3, i32::wrapping_neg(1 << k) as i64);
                     // Curiously, we don't care here what the sign of d is.
                     pos.func.dfg.replace(inst).isub(n1, t4);
                 } else {
                     // S32 div by a power-of-2
                     let t4 = pos.ins().sshr_imm(t3, k as i64);
-                    if isNeg {
+                    if is_negative {
                         pos.func.dfg.replace(inst).irsub_imm(t4, 0);
                     } else {
                         pos.func.dfg.replace(inst).copy(t4);
                     }
                 }
             } else {
                 // S32 div, rem by a non-power-of-2
                 debug_assert!(d < -2 || d > 2);
@@ -355,17 +359,17 @@ fn do_divrem_transformation(divrem_info:
                     q2
                 } else {
                     pos.ins().sshr_imm(q2, shift_by as i64)
                 };
                 let t1 = pos.ins().ushr_imm(q3, 31);
                 let qf = pos.ins().iadd(q3, t1);
                 // Now qf holds the final quotient. If necessary calculate
                 // the remainder instead.
-                if isRem {
+                if is_rem {
                     let tt = pos.ins().imul_imm(qf, d as i64);
                     pos.func.dfg.replace(inst).isub(n1, tt);
                 } else {
                     pos.func.dfg.replace(inst).copy(qf);
                 }
             }
         }
 
@@ -375,43 +379,43 @@ fn do_divrem_transformation(divrem_info:
         DivRemByConstInfo::DivS64(_n1, -1)
         | DivRemByConstInfo::RemS64(_n1, -1)
         | DivRemByConstInfo::DivS64(_n1, 0)
         | DivRemByConstInfo::RemS64(_n1, 0) => {}
 
         // S64 div by 1: identity
         // S64 rem by 1: zero
         DivRemByConstInfo::DivS64(n1, 1) | DivRemByConstInfo::RemS64(n1, 1) => {
-            if isRem {
+            if is_rem {
                 pos.func.dfg.replace(inst).iconst(I64, 0);
             } else {
                 pos.func.dfg.replace(inst).copy(n1);
             }
         }
 
         DivRemByConstInfo::DivS64(n1, d) | DivRemByConstInfo::RemS64(n1, d) => {
-            if let Some((isNeg, k)) = isPowerOf2_S64(d) {
+            if let Some((is_negative, k)) = i64_is_power_of_two(d) {
                 // k can be 63 only in the case that d is -2^63.
                 debug_assert!(k >= 1 && k <= 63);
                 let t1 = if k - 1 == 0 {
                     n1
                 } else {
                     pos.ins().sshr_imm(n1, (k - 1) as i64)
                 };
                 let t2 = pos.ins().ushr_imm(t1, (64 - k) as i64);
                 let t3 = pos.ins().iadd(n1, t2);
-                if isRem {
+                if is_rem {
                     // S64 rem by a power-of-2
                     let t4 = pos.ins().band_imm(t3, i64::wrapping_neg(1 << k));
                     // Curiously, we don't care here what the sign of d is.
                     pos.func.dfg.replace(inst).isub(n1, t4);
                 } else {
                     // S64 div by a power-of-2
                     let t4 = pos.ins().sshr_imm(t3, k as i64);
-                    if isNeg {
+                    if is_negative {
                         pos.func.dfg.replace(inst).irsub_imm(t4, 0);
                     } else {
                         pos.func.dfg.replace(inst).copy(t4);
                     }
                 }
             } else {
                 // S64 div, rem by a non-power-of-2
                 debug_assert!(d < -2 || d > 2);
@@ -430,17 +434,17 @@ fn do_divrem_transformation(divrem_info:
                     q2
                 } else {
                     pos.ins().sshr_imm(q2, shift_by as i64)
                 };
                 let t1 = pos.ins().ushr_imm(q3, 63);
                 let qf = pos.ins().iadd(q3, t1);
                 // Now qf holds the final quotient. If necessary calculate
                 // the remainder instead.
-                if isRem {
+                if is_rem {
                     let tt = pos.ins().imul_imm(qf, d);
                     pos.func.dfg.replace(inst).isub(n1, tt);
                 } else {
                     pos.func.dfg.replace(inst).copy(qf);
                 }
             }
         }
     }
@@ -765,23 +769,19 @@ fn branch_order(pos: &mut FuncCursor, cf
 pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph) {
     let _tt = timing::preopt();
     let mut pos = FuncCursor::new(func);
     while let Some(ebb) = pos.next_ebb() {
         while let Some(inst) = pos.next_inst() {
             // Apply basic simplifications.
             simplify(&mut pos, inst);
 
-            //-- BEGIN -- division by constants ----------------
-
-            let mb_dri = get_div_info(inst, &pos.func.dfg);
-            if let Some(divrem_info) = mb_dri {
+            // Try to transform divide-by-constant into simpler operations.
+            if let Some(divrem_info) = get_div_info(inst, &pos.func.dfg) {
                 do_divrem_transformation(&divrem_info, &mut pos, inst);
                 continue;
             }
 
-            //-- END -- division by constants ------------------
-
             branch_opt(&mut pos, inst);
             branch_order(&mut pos, cfg, ebb, inst);
         }
     }
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/value_label.rs
@@ -0,0 +1,268 @@
+use crate::ir::{Function, SourceLoc, Value, ValueLabel, ValueLabelAssignments, ValueLoc};
+use crate::isa::TargetIsa;
+use crate::regalloc::{Context, RegDiversions};
+use std::cmp::Ordering;
+use std::collections::{BTreeMap, HashMap};
+use std::iter::Iterator;
+use std::ops::Bound::*;
+use std::ops::Deref;
+use std::vec::Vec;
+
+/// Value location range.
+#[derive(Debug, Clone, Copy)]
+pub struct ValueLocRange {
+    pub loc: ValueLoc,
+    pub start: u32,
+    pub end: u32,
+}
+
+/// Resulting map of Value labels and their ranges/locations.
+pub type ValueLabelsRanges = HashMap<ValueLabel, Vec<ValueLocRange>>;
+
+fn build_value_labels_index<T>(func: &Function) -> BTreeMap<T, (Value, ValueLabel)>
+where
+    T: From<SourceLoc> + Deref<Target = SourceLoc> + Ord + Copy,
+{
+    if func.dfg.values_labels.is_none() {
+        return BTreeMap::new();
+    }
+    let values_labels = func.dfg.values_labels.as_ref().unwrap();
+
+    // Index values_labels by srcloc/from
+    let mut sorted = BTreeMap::new();
+    for (val, assigns) in values_labels {
+        match assigns {
+            ValueLabelAssignments::Starts(labels) => {
+                for label in labels {
+                    if label.from.is_default() {
+                        continue;
+                    }
+                    let srcloc = T::from(label.from);
+                    let label = label.label;
+                    sorted.insert(srcloc, (*val, label));
+                }
+            }
+            ValueLabelAssignments::Alias { from, value } => {
+                if from.is_default() {
+                    continue;
+                }
+                let mut aliased_value = *value;
+                while let Some(ValueLabelAssignments::Alias { value, .. }) =
+                    values_labels.get(&aliased_value)
+                {
+                    // TODO check/limit recursion?
+                    aliased_value = *value;
+                }
+                let from = T::from(*from);
+                if let Some(ValueLabelAssignments::Starts(labels)) =
+                    values_labels.get(&aliased_value)
+                {
+                    for label in labels {
+                        let srcloc = if label.from.is_default() {
+                            from
+                        } else {
+                            from.max(T::from(label.from))
+                        };
+                        let label = label.label;
+                        sorted.insert(srcloc, (*val, label));
+                    }
+                }
+            }
+        }
+    }
+    sorted
+}
+
+/// Builds ranges and location for specified value labels.
+/// The labels specified at DataFlowGraph's values_labels collection.
+pub fn build_value_labels_ranges<T>(
+    func: &Function,
+    regalloc: &Context,
+    isa: &TargetIsa,
+) -> ValueLabelsRanges
+where
+    T: From<SourceLoc> + Deref<Target = SourceLoc> + Ord + Copy,
+{
+    let values_labels = build_value_labels_index::<T>(func);
+
+    let mut ebbs = func.layout.ebbs().collect::<Vec<_>>();
+    ebbs.sort_by_key(|ebb| func.offsets[*ebb]); // Ensure inst offsets always increase
+    let encinfo = isa.encoding_info();
+    let values_locations = &func.locations;
+    let liveness_context = regalloc.liveness().context(&func.layout);
+    let liveness_ranges = regalloc.liveness().ranges();
+
+    let mut ranges = HashMap::new();
+    let mut add_range = |label, range: (u32, u32), loc: ValueLoc| {
+        if range.0 >= range.1 || !loc.is_assigned() {
+            return;
+        }
+        if !ranges.contains_key(&label) {
+            ranges.insert(label, Vec::new());
+        }
+        ranges.get_mut(&label).unwrap().push(ValueLocRange {
+            loc,
+            start: range.0,
+            end: range.1,
+        });
+    };
+
+    let mut end_offset = 0;
+    let mut tracked_values: Vec<(Value, ValueLabel, u32, ValueLoc)> = Vec::new();
+    let mut divert = RegDiversions::new();
+    for ebb in ebbs {
+        divert.clear();
+        let mut last_srcloc: Option<T> = None;
+        for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) {
+            divert.apply(&func.dfg[inst]);
+            end_offset = offset + size;
+            // Remove killed values.
+            tracked_values.retain(|(x, label, start_offset, last_loc)| {
+                let range = liveness_ranges.get(*x);
+                if range.expect("value").killed_at(inst, ebb, liveness_context) {
+                    add_range(*label, (*start_offset, end_offset), *last_loc);
+                    return false;
+                }
+                return true;
+            });
+
+            let srcloc = func.srclocs[inst];
+            if srcloc.is_default() {
+                // Don't process instructions without srcloc.
+                continue;
+            }
+            let srcloc = T::from(srcloc);
+
+            // Record and restart ranges if Value location was changed.
+            for (val, label, start_offset, last_loc) in &mut tracked_values {
+                let new_loc = divert.get(*val, values_locations);
+                if new_loc == *last_loc {
+                    continue;
+                }
+                add_range(*label, (*start_offset, end_offset), *last_loc);
+                *start_offset = end_offset;
+                *last_loc = new_loc;
+            }
+
+            // New source locations range started: abandon all tracked values.
+            if last_srcloc.is_some() && last_srcloc.as_ref().unwrap() > &srcloc {
+                for (_, label, start_offset, last_loc) in &tracked_values {
+                    add_range(*label, (*start_offset, end_offset), *last_loc);
+                }
+                tracked_values.clear();
+                last_srcloc = None;
+            }
+
+            // Get non-processed Values based on srcloc
+            let range = (
+                match last_srcloc {
+                    Some(a) => Excluded(a),
+                    None => Unbounded,
+                },
+                Included(srcloc),
+            );
+            let active_values = values_labels.range(range);
+            let active_values = active_values.filter(|(_, (v, _))| {
+                // Ignore dead/inactive Values.
+                let range = liveness_ranges.get(*v);
+                match range {
+                    Some(r) => r.reaches_use(inst, ebb, liveness_context),
+                    None => false,
+                }
+            });
+            // Append new Values to the tracked_values.
+            for (_, (val, label)) in active_values {
+                let loc = divert.get(*val, values_locations);
+                tracked_values.push((*val, *label, end_offset, loc));
+            }
+
+            last_srcloc = Some(srcloc);
+        }
+        // Finish all started ranges.
+        for (_, label, start_offset, last_loc) in &tracked_values {
+            add_range(*label, (*start_offset, end_offset), *last_loc);
+        }
+    }
+
+    // Optimize ranges in-place
+    for (_, label_ranges) in ranges.iter_mut() {
+        assert!(label_ranges.len() > 0);
+        label_ranges.sort_by(|a, b| a.start.cmp(&b.start).then_with(|| a.end.cmp(&b.end)));
+
+        // Merge ranges
+        let mut i = 1;
+        let mut j = 0;
+        while i < label_ranges.len() {
+            assert!(label_ranges[j].start <= label_ranges[i].end);
+            if label_ranges[j].loc != label_ranges[i].loc {
+                // Different location
+                if label_ranges[j].end >= label_ranges[i].end {
+                    // Consumed by previous range, skipping
+                    i += 1;
+                    continue;
+                }
+                j += 1;
+                label_ranges[j] = label_ranges[i];
+                i += 1;
+                continue;
+            }
+            if label_ranges[j].end < label_ranges[i].start {
+                // Gap in the range location
+                j += 1;
+                label_ranges[j] = label_ranges[i];
+                i += 1;
+                continue;
+            }
+            // Merge i-th and j-th ranges
+            if label_ranges[j].end < label_ranges[i].end {
+                label_ranges[j].end = label_ranges[i].end;
+            }
+            i += 1;
+        }
+        label_ranges.truncate(j + 1);
+
+        // Cut/move start position of next range, if two neighbor ranges intersect.
+        for i in 0..j {
+            if label_ranges[i].end > label_ranges[i + 1].start {
+                label_ranges[i + 1].start = label_ranges[i].end;
+                assert!(label_ranges[i + 1].start < label_ranges[i + 1].end);
+            }
+            assert!(label_ranges[i].end <= label_ranges[i + 1].start);
+        }
+    }
+    ranges
+}
+
+#[derive(Eq, Clone, Copy)]
+pub struct ComparableSourceLoc(SourceLoc);
+
+impl From<SourceLoc> for ComparableSourceLoc {
+    fn from(s: SourceLoc) -> Self {
+        ComparableSourceLoc(s)
+    }
+}
+
+impl Deref for ComparableSourceLoc {
+    type Target = SourceLoc;
+    fn deref(&self) -> &SourceLoc {
+        &self.0
+    }
+}
+
+impl PartialOrd for ComparableSourceLoc {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for ComparableSourceLoc {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.0.bits().cmp(&other.0.bits())
+    }
+}
+
+impl PartialEq for ComparableSourceLoc {
+    fn eq(&self, other: &Self) -> bool {
+        self.0 == other.0
+    }
+}
--- a/third_party/rust/cranelift-codegen/src/write.rs
+++ b/third_party/rust/cranelift-codegen/src/write.rs
@@ -1,19 +1,24 @@
 //! Converting Cranelift IR to text.
 //!
 //! The `write` module provides the `write_function` function which converts an IR `Function` to an
 //! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate.
 
 use crate::entity::SecondaryMap;
 use crate::ir::entities::AnyEntity;
-use crate::ir::{DataFlowGraph, Ebb, Function, Inst, SigRef, Type, Value, ValueDef};
+use crate::ir::{
+    DataFlowGraph, DisplayFunctionAnnotations, Ebb, Function, Inst, SigRef, Type, Value, ValueDef,
+    ValueLoc,
+};
 use crate::isa::{RegInfo, TargetIsa};
 use crate::packed_option::ReservedValue;
+use crate::value_label::ValueLabelsRanges;
 use core::fmt::{self, Write};
+use std::collections::HashSet;
 use std::string::String;
 use std::vec::Vec;
 
 /// A `FuncWriter` used to decorate functions during printing.
 pub trait FuncWriter {
     /// Write the extended basic block header for the current function.
     fn write_ebb_header(
         &mut self,
@@ -149,18 +154,22 @@ impl FuncWriter for PlainWriter {
         indent: usize,
     ) -> fmt::Result {
         write_ebb_header(w, func, isa, ebb, indent)
     }
 }
 
 /// Write `func` to `w` as equivalent text.
 /// Use `isa` to emit ISA-dependent annotations.
-pub fn write_function(w: &mut Write, func: &Function, isa: Option<&TargetIsa>) -> fmt::Result {
-    decorate_function(&mut PlainWriter, w, func, isa)
+pub fn write_function(
+    w: &mut Write,
+    func: &Function,
+    annotations: &DisplayFunctionAnnotations,
+) -> fmt::Result {
+    decorate_function(&mut PlainWriter, w, func, annotations)
 }
 
 /// Create a reverse-alias map from a value to all aliases having that value as a direct target
 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
     let mut aliases = SecondaryMap::<_, Vec<_>>::new();
     for v in func.dfg.values() {
         // VADFS returns the immediate target of an alias
         if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
@@ -172,31 +181,31 @@ fn alias_map(func: &Function) -> Seconda
 
 /// Writes `func` to `w` as text.
 /// write_function_plain is passed as 'closure' to print instructions as text.
 /// pretty_function_error is passed as 'closure' to add error decoration.
 pub fn decorate_function<FW: FuncWriter>(
     func_w: &mut FW,
     w: &mut Write,
     func: &Function,
-    isa: Option<&TargetIsa>,
+    annotations: &DisplayFunctionAnnotations,
 ) -> fmt::Result {
-    let regs = isa.map(TargetIsa::register_info);
+    let regs = annotations.isa.map(TargetIsa::register_info);
     let regs = regs.as_ref();
 
     write!(w, "function ")?;
     write_spec(w, func, regs)?;
     writeln!(w, " {{")?;
     let aliases = alias_map(func);
     let mut any = func_w.write_preamble(w, func, regs)?;
     for ebb in &func.layout {
         if any {
             writeln!(w)?;
         }
-        decorate_ebb(func_w, w, func, &aliases, isa, ebb)?;
+        decorate_ebb(func_w, w, func, &aliases, annotations, ebb)?;
         any = true;
     }
     writeln!(w, "}}")
 }
 
 //----------------------------------------------------------------------
 //
 // Function spec.
@@ -249,37 +258,93 @@ pub fn write_ebb_header(
     // Remaining arguments.
     for arg in args {
         write!(w, ", ")?;
         write_arg(w, func, regs, arg)?;
     }
     writeln!(w, "):")
 }
 
+fn write_valueloc(w: &mut Write, loc: &ValueLoc, regs: &RegInfo) -> fmt::Result {
+    match loc {
+        ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(*r)),
+        ValueLoc::Stack(ss) => write!(w, "{}", ss),
+        ValueLoc::Unassigned => write!(w, "?"),
+    }
+}
+
+fn write_value_range_markers(
+    w: &mut Write,
+    val_ranges: &ValueLabelsRanges,
+    regs: &RegInfo,
+    offset: u32,
+    indent: usize,
+) -> fmt::Result {
+    let mut result = String::new();
+    let mut shown = HashSet::new();
+    for (val, rng) in val_ranges {
+        for i in (0..rng.len()).rev() {
+            if rng[i].start == offset {
+                write!(&mut result, " {}@", val)?;
+                write_valueloc(&mut result, &rng[i].loc, regs)?;
+                shown.insert(val);
+                break;
+            }
+        }
+    }
+    for (val, rng) in val_ranges {
+        for i in (0..rng.len()).rev() {
+            if rng[i].end == offset && !shown.contains(val) {
+                write!(&mut result, " {}\u{2620}", val)?;
+                break;
+            }
+        }
+    }
+    if result.len() > 0 {
+        writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?;
+    }
+    Ok(())
+}
+
 fn decorate_ebb<FW: FuncWriter>(
     func_w: &mut FW,
     w: &mut Write,
     func: &Function,
     aliases: &SecondaryMap<Value, Vec<Value>>,
-    isa: Option<&TargetIsa>,
+    annotations: &DisplayFunctionAnnotations,
     ebb: Ebb,
 ) -> fmt::Result {
     // Indent all instructions if any encodings are present.
     let indent = if func.encodings.is_empty() && func.srclocs.is_empty() {
         4
     } else {
         36
     };
+    let isa = annotations.isa;
 
     func_w.write_ebb_header(w, func, isa, ebb, indent)?;
     for a in func.dfg.ebb_params(ebb).iter().cloned() {
         write_value_aliases(w, aliases, a, indent)?;
     }
-    for inst in func.layout.ebb_insts(ebb) {
-        func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
+
+    if isa.is_some() && !func.offsets.is_empty() {
+        let encinfo = isa.unwrap().encoding_info();
+        let regs = &isa.unwrap().register_info();
+        for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) {
+            func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
+            if size > 0 {
+                if let Some(val_ranges) = annotations.value_ranges {
+                    write_value_range_markers(w, val_ranges, regs, offset + size, indent)?;
+                }
+            }
+        }
+    } else {
+        for inst in func.layout.ebb_insts(ebb) {
+            func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
+        }
     }
 
     Ok(())
 }
 
 //----------------------------------------------------------------------
 //
 // Instructions
--- a/third_party/rust/cranelift-frontend/.cargo-checksum.json
+++ b/third_party/rust/cranelift-frontend/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"c92f07d9959d10331c6b4770a4db12f706927a18897dfed45472abcd6e58190e","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"dea43e8044284df50f8b8772e9b48ba8b109b45c74111ff73619775d57ad8d67","src/frontend.rs":"f3ad024e4895eddf83c8fe19c93ae37709a6bf27db2e1beef153fd742d99defa","src/lib.rs":"1cc2e7aaffa45bccea9e59fcc9d9c5d295a9f7adacd6bd55933834e20e969aef","src/ssa.rs":"88cb07071943f3e72a91c91afb58960689b4d9c56352b3bb7cd5d69288066190","src/switch.rs":"b8f337966b540254feb5f979b4a146f5ef69ae199864da6332c9d7513ff3ec8b","src/variable.rs":"f082efaa4b2d3c5eb48f6344149408074e1e15cb581f7a63f549313c7a1037be"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"c92f07d9959d10331c6b4770a4db12f706927a18897dfed45472abcd6e58190e","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"dea43e8044284df50f8b8772e9b48ba8b109b45c74111ff73619775d57ad8d67","src/frontend.rs":"4df0a0dedbb26c7fdc5d561b6609ceaefb396669b21a1db484638c074914a74d","src/lib.rs":"1cc2e7aaffa45bccea9e59fcc9d9c5d295a9f7adacd6bd55933834e20e969aef","src/ssa.rs":"88cb07071943f3e72a91c91afb58960689b4d9c56352b3bb7cd5d69288066190","src/switch.rs":"b8f337966b540254feb5f979b4a146f5ef69ae199864da6332c9d7513ff3ec8b","src/variable.rs":"f082efaa4b2d3c5eb48f6344149408074e1e15cb581f7a63f549313c7a1037be"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-frontend/src/frontend.rs
+++ b/third_party/rust/cranelift-frontend/src/frontend.rs
@@ -4,17 +4,17 @@ use crate::variable::Variable;
 use cranelift_codegen::cursor::{Cursor, FuncCursor};
 use cranelift_codegen::entity::{EntitySet, SecondaryMap};
 use cranelift_codegen::ir;
 use cranelift_codegen::ir::function::DisplayFunction;
 use cranelift_codegen::ir::{
     types, AbiParam, DataFlowGraph, Ebb, ExtFuncData, ExternalName, FuncRef, Function, GlobalValue,
     GlobalValueData, Heap, HeapData, Inst, InstBuilder, InstBuilderBase, InstructionData,
     JumpTable, JumpTableData, LibCall, MemFlags, SigRef, Signature, StackSlot, StackSlotData, Type,
-    Value,
+    Value, ValueLabel, ValueLabelAssignments, ValueLabelStart,
 };
 use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa};
 use cranelift_codegen::packed_option::PackedOption;
 use std::vec::Vec;
 
 /// Structure used for translating a series of functions into Cranelift IR.
 ///
 /// In order to reduce memory reallocations when compiling multiple functions,
@@ -328,16 +328,38 @@ impl<'a> FunctionBuilder<'a> {
             val
         );
 
         self.func_ctx
             .ssa
             .def_var(var, val, self.position.basic_block.unwrap());
     }
 
+    /// Set label for Value
+    pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
+        if let Some(values_labels) = self.func.dfg.values_labels.as_mut() {
+            use std::collections::hash_map::Entry;
+
+            let start = ValueLabelStart {
+                from: self.srcloc,
+                label,
+            };
+
+            match values_labels.entry(val) {
+                Entry::Occupied(mut e) => match e.get_mut() {
+                    ValueLabelAssignments::Starts(starts) => starts.push(start),
+                    _ => panic!("Unexpected ValueLabelAssignments at this stage"),
+                },
+                Entry::Vacant(e) => {
+                    e.insert(ValueLabelAssignments::Starts(vec![start]));
+                }
+            }
+        }
+    }
+
     /// Creates a jump table in the function, to be used by `br_table` instructions.
     pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
         self.func.create_jump_table(data)
     }
 
     /// Creates a stack slot in the function, to be used by `stack_load`, `stack_store` and
     /// `stack_addr` instructions.
     pub fn